Files
matrix-screen-controller/核桃派软件源代码/app/static/sortable-card.js
T

80 lines
2.6 KiB
JavaScript

export const DRAG_START_GUARD_PX = 8;
const INTERACTIVE_SELECTOR = "button, input, select, textarea, a[href], [contenteditable]:not([contenteditable='false'])";
export function pointInsideExpandedRect(x, y, rect, padding = DRAG_START_GUARD_PX) {
return x >= rect.left - padding
&& x <= rect.right + padding
&& y >= rect.top - padding
&& y <= rect.bottom + padding;
}
export function isProtectedDragStart(card, event, padding = DRAG_START_GUARD_PX) {
const target = typeof Element !== "undefined" && event.target instanceof Element ? event.target : null;
if (target?.closest(INTERACTIVE_SELECTOR)) return true;
if (!Number.isFinite(event.clientX) || !Number.isFinite(event.clientY)) return false;
return [...card.querySelectorAll(INTERACTIVE_SELECTOR)].some((control) => (
pointInsideExpandedRect(event.clientX, event.clientY, control.getBoundingClientRect(), padding)
));
}
export function protectCardDragStart(card, { locked = false } = {}) {
let dragging = false;
let restoreListeners = null;
const restore = () => {
restoreListeners?.();
restoreListeners = null;
if (!locked && !dragging) card.draggable = true;
};
card.draggable = !locked;
card.addEventListener("pointerdown", (event) => {
if (locked || dragging) return;
restore();
if (!isProtectedDragStart(card, event)) {
card.draggable = true;
return;
}
card.draggable = false;
const pointerId = event.pointerId;
const finish = (finishEvent) => {
if (finishEvent.pointerId !== pointerId) return;
queueMicrotask(restore);
};
window.addEventListener("pointerup", finish, true);
window.addEventListener("pointercancel", finish, true);
restoreListeners = () => {
window.removeEventListener("pointerup", finish, true);
window.removeEventListener("pointercancel", finish, true);
};
}, true);
card.addEventListener("dragstart", (event) => {
if (locked || !card.draggable || isProtectedDragStart(card, event)) {
event.preventDefault();
restore();
return;
}
dragging = true;
restoreListeners?.();
restoreListeners = null;
card.classList.add("is-dragging");
});
card.addEventListener("dragend", () => {
dragging = false;
card.classList.remove("is-dragging");
restore();
});
return { restore };
}
export function reorderByKey(items, sourceKey, targetKey) {
const order = [...items];
const from = order.indexOf(sourceKey);
const target = order.indexOf(targetKey);
if (from < 0 || target < 0 || from === target) return null;
order.splice(target, 0, order.splice(from, 1)[0]);
return order;
}