stylus/vendor/draggable-list/draggable-list.iife.js
2022-03-31 14:00:12 +03:00

167 lines
4.9 KiB
JavaScript

var DraggableList = (function () {
'use strict';
/* eslint-env browser */
const CLS_TRANSFORMED = 'draggable-list-transformed';
function posToIndex(rects, startIndex, y, bound) {
if (y < rects[0].top && bound) return startIndex;
for (let i = 0; i < startIndex; i++) {
if (rects[i].bottom < y) continue;
return i;
}
if (y > rects[rects.length - 1].bottom && bound) return startIndex;
for (let i = rects.length - 1; i > startIndex; i--) {
if (rects[i].top > y) continue;
return i;
}
return startIndex;
}
function applyTransform(list, startIndex, oldIndex, newIndex, len) {
if (newIndex > oldIndex) {
transform(false, oldIndex, Math.min(startIndex - 1, newIndex - 1));
if (startIndex < list.length - 1) {
transform(true, Math.max(oldIndex + 1, startIndex + 1), newIndex, "translateY(".concat(-len, "px)"));
}
} else {
transform(false, Math.max(startIndex + 1, newIndex + 1), oldIndex);
if (startIndex > 0) {
transform(true, newIndex, Math.min(oldIndex - 1, startIndex - 1), "translateY(".concat(len, "px)"));
}
}
function transform(state, p, q, style) {
for (let i = p; i <= q; i++) {
if (state && !list[i].classList.contains(CLS_TRANSFORMED)) {
list[i].classList.add(CLS_TRANSFORMED);
list[i].style.transform = style;
} else if (!state && list[i].classList.contains(CLS_TRANSFORMED)) {
list[i].classList.remove(CLS_TRANSFORMED);
list[i].style = '';
}
}
}
}
function DraggableList(el, {
bound,
scrollContainer
} = {}) {
for (const c of el.children) {
c.draggable = true;
}
new MutationObserver(records => {
for (const r of records) {
for (const n of r.addedNodes) {
n.draggable = true;
}
}
}).observe(el, {
childList: true
});
let startPos = null;
let startIndex = 0;
let dragOverIndex = 0;
let dragOverPos = null;
let rects = [];
let dragTarget = null;
let dropped = false;
let itemSize = 0;
el.addEventListener('dragstart', e => {
if (e.target.parentNode !== el) return;
dragTarget = e.target;
dropped = false;
const scrollLeft = scrollContainer ? scrollContainer.scrollLeft : 0;
const scrollTop = scrollContainer ? scrollContainer.scrollTop : 0;
startPos = {
x: e.pageX + scrollLeft,
y: e.pageY + scrollTop
};
startIndex = [...el.children].indexOf(e.target);
dragOverIndex = startIndex;
dragOverPos = startPos;
rects = [...el.children].map(el => {
const r = el.getBoundingClientRect();
return {
top: r.top + window.scrollY + scrollTop,
bottom: r.bottom + window.scrollY + scrollTop
};
});
itemSize = startIndex + 1 < rects.length ? rects[startIndex + 1].top - rects[startIndex].top : startIndex > 0 ? rects[startIndex].bottom - rects[startIndex - 1].bottom : 0;
dragTarget.classList.add('draggable-list-target');
el.classList.add('draggable-list-dragging');
dispatch(e, 'd:dragstart');
});
el.addEventListener('dragenter', e => {
if (dragTarget) {
e.preventDefault();
dispatch(e, 'd:dragmove');
}
});
el.addEventListener('dragover', e => {
if (!dragTarget) return;
e.preventDefault();
const scrollLeft = scrollContainer ? scrollContainer.scrollLeft : 0;
const scrollTop = scrollContainer ? scrollContainer.scrollTop : 0;
const newPos = {
x: e.pageX + scrollLeft,
y: e.pageY + scrollTop
};
const newIndex = posToIndex(rects, startIndex, newPos.y, bound);
applyTransform(el.children, startIndex, dragOverIndex, newIndex, itemSize);
dragOverIndex = newIndex;
dragOverPos = newPos;
dispatch(e, 'd:dragmove');
});
document.addEventListener('dragend', e => {
if (!dragTarget) return;
for (const c of el.children) {
c.classList.remove(CLS_TRANSFORMED);
c.style = '';
}
dragTarget.classList.remove('draggable-list-target');
el.classList.remove('draggable-list-dragging');
dispatch(e, 'd:dragend', {
originalIndex: startIndex,
spliceIndex: dragOverIndex,
insertBefore: dragOverIndex < startIndex ? el.children[dragOverIndex] : el.children[dragOverIndex + 1],
dropped
});
dragTarget = null;
});
el.addEventListener('drop', e => {
if (dragTarget) {
dropped = true;
e.preventDefault();
}
});
function dispatch(e, name, props) {
const detail = {
origin: e,
startPos,
currentPos: dragOverPos,
dragTarget
};
if (props) {
Object.assign(detail, props);
}
el.dispatchEvent(new CustomEvent(name, {
detail
}));
}
}
return DraggableList;
})();