account page: can now reorder entries under shops sections by drag and drop
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
(function(){
|
||||
/**
|
||||
* @type {object}
|
||||
*/
|
||||
const htmx = window.htmx;
|
||||
|
||||
htmx.defineExtension("drag", {
|
||||
init: (node) => {
|
||||
// ideally it should only start listening to elements with hx-ext="drag"
|
||||
// I assume there is a better way I can bind than this?
|
||||
document.addEventListener("dragstart", DragStart);
|
||||
document.addEventListener("dragover", DragOver);
|
||||
document.addEventListener("dragleave", DragLeave);
|
||||
document.addEventListener("dragend", DragEnd);
|
||||
document.addEventListener("drop", Drop);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* the most recent element a drag event started on
|
||||
* @type {Element|null}
|
||||
*/
|
||||
let dragging = null;
|
||||
|
||||
/**
|
||||
* Get the parent element which matches the selector
|
||||
* @param {EventTarget|null} target
|
||||
* @param {string} selector
|
||||
* @returns {Element | null}
|
||||
*/
|
||||
function GetTarget(target, selector) {
|
||||
if (target == null) return null;
|
||||
if (!(target instanceof Element)) return null;
|
||||
|
||||
target = target.closest(`[${selector}],[data-${selector}]`);
|
||||
if (target == null) return null;
|
||||
if (!(target instanceof Element)) return null;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attribute allowing `data-` fallback
|
||||
* @param {Element | null} target
|
||||
* @param {string} name
|
||||
* @returns {string | null}
|
||||
*/
|
||||
function GetAttribute(target, name) {
|
||||
if (target === null) return null;
|
||||
return target.getAttribute(name) || target.getAttribute("data-"+name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DragEvent} ev
|
||||
*/
|
||||
function DragStart(ev) {
|
||||
if (!ev.dataTransfer) return;
|
||||
|
||||
const target = GetTarget(ev.target, "hx-drag");
|
||||
if (!target) return null;
|
||||
|
||||
const data = GetAttribute(target, "hx-drag");
|
||||
if (data === null) return;
|
||||
|
||||
target.classList.add("hx-drag");
|
||||
dragging = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DragEvent} ev
|
||||
*/
|
||||
function DragOver(ev) {
|
||||
if (!dragging) return;
|
||||
|
||||
const target = GetTarget(ev.target, "hx-drop");
|
||||
if (!target) return;
|
||||
ev.preventDefault();
|
||||
|
||||
target.classList.add("hx-drag-over");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DragEvent} ev
|
||||
*/
|
||||
function DragLeave(ev) {
|
||||
if (!dragging) return;
|
||||
|
||||
const target = GetTarget(ev.target, "hx-drop");
|
||||
if (!target) return;
|
||||
|
||||
target.classList.remove("hx-drag-over");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DragEvent} ev
|
||||
*/
|
||||
function DragEnd() {
|
||||
if (!dragging) return;
|
||||
dragging.classList.remove("hx-drag");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DragEvent} ev
|
||||
*/
|
||||
async function Drop(ev) {
|
||||
if (!dragging) return;
|
||||
|
||||
const drag = dragging;
|
||||
const drop = GetTarget(ev.target, "hx-drop");
|
||||
if (!drop) return;
|
||||
drop.classList.add("hx-drop");
|
||||
drop.classList.remove("hx-drag-over");
|
||||
|
||||
const dragAttr = GetAttribute(drag, "hx-drag");
|
||||
const dropAttr = GetAttribute(drop, "hx-drop");
|
||||
|
||||
console.log('dragAttr:', dragAttr);
|
||||
console.log('dropAttr:', dropAttr);
|
||||
|
||||
const dragVals = JSON.parse(GetAttribute(drag, "hx-drag") || "{}");
|
||||
const dropVals = JSON.parse(GetAttribute(drop, "hx-drop") || "{}");
|
||||
|
||||
const precedence = GetAttribute(drag, "hx-drag-sync") || GetAttribute(drag, "hx-drop-sync");
|
||||
const dragFirst = precedence === "drag";
|
||||
const sync = !!precedence;
|
||||
|
||||
drop.classList.add("htmx-request");
|
||||
drag.classList.add("htmx-request");
|
||||
const queue = dragFirst ? [drag, drop] : [drop, drag];
|
||||
for (const elm of queue) {
|
||||
const promise = RunDragDrop(elm, elm === drag, dragVals, dropVals);
|
||||
if (sync) await promise;
|
||||
}
|
||||
|
||||
drag.classList.remove("hx-drag");
|
||||
drop.classList.remove("hx-drop");
|
||||
dragging = null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Element} source
|
||||
* @param {boolean} isDrag
|
||||
* @param {object} dragVals
|
||||
* @param {object} dropVals
|
||||
* @returns
|
||||
*/
|
||||
async function RunDragDrop(source, isDrag, dragVals, dropVals) {
|
||||
if (!document.body.contains(source)) return;
|
||||
|
||||
let method, action, values;
|
||||
if (isDrag) {
|
||||
action = GetAttribute(source, "hx-drag-action");
|
||||
method = GetAttribute(source, "hx-drag-method") || "PUT";
|
||||
values = Object.assign({}, dropVals, dragVals);
|
||||
} else {
|
||||
action = GetAttribute(source, "hx-drop-action");
|
||||
method = GetAttribute(source, "hx-drop-method") || "PUT";
|
||||
values = Object.assign({}, dragVals, dropVals);
|
||||
}
|
||||
|
||||
console.log('action:', action)
|
||||
|
||||
if (action === null) return;
|
||||
|
||||
try { // don't let one failure cascade to the other ajax
|
||||
await htmx.ajax(method, action, { source, values });
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
})()
|
||||
Reference in New Issue
Block a user