SortableJS: сложно переместить элемент в родительский элемент при перетаскивании последнего элемента обернутой строкиHtml

Программисты Html
Ответить
Anonymous
 SortableJS: сложно переместить элемент в родительский элемент при перетаскивании последнего элемента обернутой строки

Сообщение Anonymous »

Я использую SortableJS для создания горизонтального вложенного макета с возможностью перетаскивания.

Списки представляют собой гибкие контейнеры с flex-wrap: Wrap, поэтому элементы размещаются в строках и могут переноситься на следующую строку.
Элементы можно перетаскивать между списками, а также в другие элементы, которые действуют как родительские (они содержат собственный вложенный список). В целом это работает нормально, но есть одна UX-проблема, которую я не могу понять, как правильно решить.
Когда я хватаю самый правый элемент строки и пытаюсь поместить его в родительский элемент, расположенный в следующей строке, макет меняется во время перетаскивания. Как только перетаскиваемый элемент покидает исходное положение, flexbox пересчитывает макет, и целевой родительский элемент может переместиться в другую строку. Визуально я стремлюсь к одной позиции, но во время перетаскивания элемент, который я хочу поместить, смещается под курсором, из-за чего очень сложно попасть в предполагаемый родительский элемент.
Чтобы уменьшить скачки макета, я попробовал использовать невидимую прокладку, которая удерживает исходный контейнер от разрушения во время перетаскивания элемента. Прокладка вставляется только тогда, когда перетаскивание покидает исходную иерархию (а не при перемещении внутри одной и той же родительской/дочерней структуры). В некоторых случаях это помогает, но все равно не полностью предотвращает смещение целевого родителя при использовании обернутых строк.

Код: Выделить всё

const containers = document.querySelectorAll(".list-group");

let spacer = null;
let originContainer = null;
let originNextSibling = null;

containers.forEach((container) => {
new Sortable(container, {
group: "nested",
direction: "horizontal",
animation: 150,

swapThreshold: 0.6,
invertSwap: true,
emptyInsertThreshold: 10,
fallbackOnBody: true,

onStart(evt) {
originContainer = evt.from;
originNextSibling = evt.item.nextSibling;

const item = evt.item;
const rect = item.getBoundingClientRect();
const style = getComputedStyle(item);

spacer = document.createElement("div");
spacer.className = "layout-preserver";
spacer.style.width = rect.width + "px";
spacer.style.height = rect.height + "px";
spacer.style.margin = style.margin;
spacer.style.display = style.display;

document.body.style.cursor = "grabbing";
},

onChange(evt) {
if (!spacer) return;

const toContainer = evt.to;
const isHierarchyMove =
originContainer === toContainer ||
originContainer.contains(toContainer) ||
toContainer.contains(originContainer);

if (isHierarchyMove) {
if (spacer.parentNode) {
spacer.remove();
}
} else {
if (!spacer.parentNode && originContainer) {
originContainer.insertBefore(spacer, originNextSibling);
}
}
},

onEnd() {
if (spacer &&  spacer.parentNode) {
spacer.remove();
}

spacer = null;
originContainer = null;
originNextSibling = null;

document.body.style.cursor = "default";
}
});
});

Код: Выделить всё

body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
padding: 20px;
background-color: #f3f3f3;
}

h1 {
text-align: center;
margin-bottom: 30px;
}

/* ------------------------------
LIST CONTAINERS
------------------------------ */

.list-group {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-start;
align-content: flex-start;

gap: 10px;
padding: 15px;

min-height: 80px;
background-color: #e0e0e0;
border-radius: 8px;
border: 1px dashed #aaa;
}

/* Nested container */
.nested-list {
margin-top: 10px;
min-height: 20px;
padding: 10px;

background-color: rgba(255, 255, 255, 0.6);
border: 1px dotted #999;
}

/* ------------------------------
ITEMS
------------------------------ */

.list-item {
position: relative;
display: flex;
flex-direction: column;

padding: 15px;
min-width: 50px;

background-color: #fff;
border: 1px solid #ccc;
border-radius: 4px;

cursor: grab;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
}

/* Nested items */
.list-item.nested {
background-color: #d1e7dd;
border-color: #a3cfbb;
}

/* Parent items */
.parent-item {
background-color: #fff3cd;
border-color: #ffecb5;
}

/* ------------------------------
SORTABLE HELPERS
------------------------------ */

/* Ghost (placeholder from SortableJS) */
.sortable-ghost {
visibility: hidden;
}

/* Dragging element */
.sortable-drag {
cursor: grabbing;
opacity: 0.9;
}

/* Layout spacer (our placeholder) */
.layout-preserver {
visibility: hidden;
pointer-events: none;
flex: 0 0 auto;
box-sizing: border-box;
}

Код: Выделить всё



Horizontal Nested Sortable








Nested Sortable




Item element 1
Item element 2




[b]Item 3 (Parent)[/b]



Sub-item 3.1
Sub-item 3.2
Sub-item 3.3
Sub-item 3.4



Item element 4




[b]Item 5 (Parent)[/b]

Item element 6










Как можно предотвратить такое смещение макета при использовании SortableJS с горизонтальными списками и гибкой оберткой?

Есть ли лучший подход, чем использование разделителя, чтобы сохранить визуальную стабильность целевых элементов во время перетаскивания?

Подробнее здесь: https://stackoverflow.com/questions/798 ... -item-of-a
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Html»