Files
olivv-cs-ComfyUI-FunPack/app/ui/composer/internals/dragReorder.js
T

47 lines
2.3 KiB
JavaScript

// Reordering a row of id-tagged children by drag, shared by every element that
// has one (the scene strip, the timeline track).
//
// Delegated on the container: cells are torn down and rebuilt on every redraw,
// and listeners bound to them would have to be reattached every time. One set
// here survives all of that.
//
// The dragged item's id lives on `dataTransfer`, not in a closure variable. A
// closure var tracks by NOTHING the browser understands, so nothing keeps it in
// step with the actual drag session: `dragend` fires on the ORIGINAL source
// element, and a redraw mid-drag (an unrelated remove/undo firing on its own
// input channel while the mouse is still held down) can detach that element
// from the tree -- its `dragend` then never bubbles here, and a closure var set
// at dragstart is never cleared, ready to be picked up by the next unrelated
// drop that lands on this row from somewhere else entirely. `dataTransfer` has
// no such gap: it belongs to the actual OS-level drag session, unaffected by
// which DOM node currently receives the events, and a drop from any OTHER
// session simply never had this MIME type set.
const MIME = "text/x-funpack-reorder";
/** wireDragReorder(node, ".cx-cell-selector", (draggedId, targetId) => {}) */
export function wireDragReorder(node, cellSelector, onReorder) {
const cellOf = (e) => e.target.closest(cellSelector);
node.addEventListener("dragstart", (e) => {
const cell = cellOf(e);
if (!cell || !e.dataTransfer) return;
e.dataTransfer.effectAllowed = "move";
e.dataTransfer.setData(MIME, cell.dataset.id || "");
cell.classList.add("cx-dragging");
});
node.addEventListener("dragend", (e) => { cellOf(e)?.classList.remove("cx-dragging"); });
node.addEventListener("dragover", (e) => {
// `getData` only returns the real value on "drop" (a browser security
// restriction during drag) -- `types` is what dragover can actually read.
if (e.dataTransfer && e.dataTransfer.types.includes(MIME)) e.preventDefault();
});
node.addEventListener("drop", (e) => {
if (!e.dataTransfer) return;
const draggedId = e.dataTransfer.getData(MIME);
if (!draggedId) return;
e.preventDefault();
const cell = cellOf(e);
const targetId = cell ? cell.dataset.id : null;
if (onReorder && targetId && targetId !== draggedId) onReorder(draggedId, targetId);
});
}