147 lines
3.4 KiB
JavaScript
147 lines
3.4 KiB
JavaScript
/* global VT */
|
|
window.VT = window.VT || {};
|
|
|
|
VT.AppSortable = function (el, options) {
|
|
var placeholder;
|
|
var placeholderSource;
|
|
var horizontal = options.direction === 'horizontal';
|
|
var currentIndex = -1;
|
|
|
|
el.addEventListener('draggableStart', function (e) {
|
|
e.detail.image.addEventListener('draggableCancel', cleanUp);
|
|
});
|
|
|
|
el.addEventListener('draggableOver', function (e) {
|
|
maybeDispatchUpdate(calculateIndex(e.detail.image), e);
|
|
});
|
|
|
|
el.addEventListener('draggableLeave', function (e) {
|
|
maybeDispatchUpdate(-1, e);
|
|
});
|
|
|
|
el.addEventListener('draggableDrop', function (e) {
|
|
el.dispatchEvent(
|
|
new CustomEvent('sortableDrop', {
|
|
detail: buildDetail(e),
|
|
bubbles: true,
|
|
})
|
|
);
|
|
});
|
|
|
|
el.addEventListener('sortableUpdate', function (e) {
|
|
if (!placeholder) {
|
|
e.detail.setPlaceholder(e.detail.originalEvent.detail.imageSource);
|
|
}
|
|
|
|
if (e.detail.index >= 0) {
|
|
insertPlaceholder(e.detail.index);
|
|
} else {
|
|
removePlaceholder();
|
|
}
|
|
|
|
removeByKey(e.detail.data.key);
|
|
});
|
|
|
|
el.addEventListener('sortableDrop', cleanUp);
|
|
|
|
function maybeDispatchUpdate(index, originalEvent) {
|
|
if (index !== currentIndex) {
|
|
currentIndex = index;
|
|
|
|
el.dispatchEvent(
|
|
new CustomEvent('sortableUpdate', {
|
|
detail: buildDetail(originalEvent),
|
|
bubbles: true,
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
function cleanUp() {
|
|
removePlaceholder();
|
|
placeholder = null;
|
|
placeholderSource = null;
|
|
currentIndex = -1;
|
|
}
|
|
|
|
function buildDetail(e) {
|
|
var detail = {
|
|
data: e.detail.data,
|
|
index: currentIndex,
|
|
placeholder: placeholder,
|
|
setPlaceholder: function (source) {
|
|
setPlaceholder(source);
|
|
detail.placeholder = placeholder;
|
|
},
|
|
originalEvent: e,
|
|
};
|
|
|
|
return detail;
|
|
}
|
|
|
|
function setPlaceholder(source) {
|
|
if (placeholderSource === source) return;
|
|
placeholderSource = source;
|
|
|
|
removePlaceholder();
|
|
|
|
placeholder = placeholderSource.cloneNode(true);
|
|
placeholder.classList.add('-placeholder');
|
|
placeholder.removeAttribute('data-key');
|
|
}
|
|
|
|
function insertPlaceholder(index) {
|
|
if (placeholder && el.children[index] !== placeholder) {
|
|
if (placeholder.parentNode === el) el.removeChild(placeholder);
|
|
el.insertBefore(placeholder, el.children[index]);
|
|
}
|
|
}
|
|
|
|
function removePlaceholder() {
|
|
if (placeholder && placeholder.parentNode) {
|
|
placeholder.parentNode.removeChild(placeholder);
|
|
}
|
|
}
|
|
|
|
function removeByKey(key) {
|
|
for (var i = 0, l = el.children.length; i < l; ++i) {
|
|
var child = el.children[i];
|
|
|
|
if (child && child.dataset.key === key) {
|
|
el.removeChild(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
function calculateIndex(image) {
|
|
if (el.children.length === 0) return 0;
|
|
|
|
var isBefore = horizontal ? isLeft : isAbove;
|
|
var rect = image.getBoundingClientRect();
|
|
var p = 0;
|
|
|
|
for (var i = 0, l = el.children.length; i < l; ++i) {
|
|
var child = el.children[i];
|
|
|
|
if (isBefore(rect, child.getBoundingClientRect())) return i - p;
|
|
if (child === placeholder) p = 1;
|
|
}
|
|
|
|
return el.children.length - p;
|
|
}
|
|
|
|
function isAbove(rectA, rectB) {
|
|
return (
|
|
rectA.top + (rectA.bottom - rectA.top) / 2 <=
|
|
rectB.top + (rectB.bottom - rectB.top) / 2
|
|
);
|
|
}
|
|
|
|
function isLeft(rectA, rectB) {
|
|
return (
|
|
rectA.left + (rectA.right - rectA.left) / 2 <=
|
|
rectB.left + (rectB.right - rectB.left) / 2
|
|
);
|
|
}
|
|
};
|