Draggable от Shopify предоставляет мощный инструмент для организации интерактивных перетаскиваемых элементов, и поддержка вложенных droppable областей является одним из наиболее гибких и продвинутых возможностей библиотеки. Вложенные области позволяют создавать сложные иерархические структуры, где один элемент может быть перемещен не только в отдельную область, но и в область внутри другой droppable контейнерной структуры.
const droppableOuter = new Draggable.Droppable(document.querySelectorAll('.outer-container'), {
draggable: '.draggable-item'
});
const droppableInner = new Draggable.Droppable(document.querySelectorAll('.inner-container'), {
draggable: '.draggable-item'
});
Здесь .inner-container находится внутри
.outer-container, что создаёт вложенную структуру.
Определение приоритетов Когда элемент перетаскивается над несколькими droppable областями, библиотека определяет активный контейнер, исходя из приоритета вложенности: элемент сначала проверяется на попадание в внутренний контейнер, затем во внешний.
События на уровне контейнеров Для управления поведением вложенных областей используются стандартные события Draggable:
drag:startdrag:movedrag:overdrag:outdrag:stopВложенные контейнеры позволяют подписываться на эти события независимо друг от друга, что дает возможность реализовать различное поведение для внутреннего и внешнего droppable.
HTML структура:
<div class="outer-container">
<div class="inner-container">
<div class="draggable-item">Элемент 1</div>
<div class="draggable-item">Элемент 2</div>
</div>
<div class="draggable-item">Элемент 3</div>
</div>
Инициализация Draggable:
import { Draggable } from '@shopify/draggable';
const draggable = new Draggable.Sortable(document.querySelectorAll('.outer-container'), {
draggable: '.draggable-item',
mirror: {
constrainDimensions: true
}
});
draggable.on('drag:start', (evt) => {
console.log('Начало перетаскивания:', evt.source);
});
draggable.on('drag:over', (evt) => {
console.log('Элемент над областью:', evt.over);
});
draggable.on('drag:stop', (evt) => {
console.log('Элемент отпущен:', evt.source);
});
В данном примере .inner-container автоматически
определяется как droppable область, так как она содержит draggable
элементы. Это позволяет элементам перемещаться внутрь вложенного
контейнера и обратно во внешний.
Вложенные droppable области могут создавать конфликтные ситуации,
когда элемент одновременно находится над несколькими контейнерами. Для
этого используется свойство overlapThreshold:
const droppableInner = new Draggable.Droppable(document.querySelectorAll('.inner-container'), {
draggable: '.draggable-item',
overlapThreshold: 0.5
});
overlapThreshold задаёт процент пересечения элемента с
droppable областью, необходимый для того, чтобы она считалась активной.
Вложенные контейнеры обычно используют более высокий порог для
внутренних областей, чтобы предотвратить случайное срабатывание внешнего
контейнера.
Draggable поддерживает динамическое создание droppable контейнеров после инициализации. Это важно при работе с интерфейсами, где структура DOM может меняться:
const newInner = document.createElement('div');
newInner.classList.add('inner-container');
document.querySelector('.outer-container').appendChild(newInner);
const droppableNewInner = new Draggable.Droppable([newInner], {
draggable: '.draggable-item'
});
Теперь newInner ведёт себя как полноценная вложенная
droppable область без перезапуска основной Draggable и позволяет
перемещать элементы как внутрь, так и наружу.
Для вложенных droppable областей особенно важно различать внешний и
внутренний контейнер визуально. Используются CSS классы
draggable--over и draggable--over-inner (имя
класса можно задать самостоятельно через опцию
classes):
const droppableInner = new Draggable.Droppable(document.querySelectorAll('.inner-container'), {
draggable: '.draggable-item',
classes: {
over: 'draggable--over-inner'
}
});
При перетаскивании элемент подсвечивается по отдельным стилям для внутреннего контейнера, что помогает визуально ориентироваться в сложной иерархии.
Для точного управления вложенными droppable областями можно использовать цепочку событий:
draggable.on('drag:over', (evt) => {
if(evt.over.classList.contains('inner-container')){
console.log('Элемент над внутренней областью');
} else {
console.log('Элемент над внешней областью');
}
});
draggable.on('drag:out', (evt) => {
console.log('Элемент покинул область:', evt.over);
});
Это позволяет реализовать функционал, например: изменение состояния родителя, подсветку пути перемещения, динамическое открытие/закрытие вложенных областей и другие сложные сценарии UX.
overlapThreshold для контроля срабатывания
внутренних и внешних областей.drag:over и
drag:out для управления динамическим поведением при
вложенных перемещениях.Эти подходы позволяют создавать сложные интерактивные интерфейсы с полностью контролируемыми вложенными droppable областями, обеспечивая удобство пользователя и предсказуемость поведения перетаскиваемых элементов.