2024-08-31 01:30:30 +01:00
|
|
|
/**
|
2024-08-31 15:55:45 +01:00
|
|
|
* Creates a reorderable list from any `container` and viable list item selector.
|
|
|
|
|
*
|
2024-08-31 01:30:30 +01:00
|
|
|
* This function is absolute magic and I love it
|
|
|
|
|
*
|
|
|
|
|
* Example:
|
|
|
|
|
* ```html
|
|
|
|
|
* <ul id="list">
|
|
|
|
|
* <li>Item 1</li>
|
|
|
|
|
* <li>Item 2</li>
|
|
|
|
|
* <li>Item 3</li>
|
|
|
|
|
* </ul>
|
|
|
|
|
* ```
|
|
|
|
|
* ```js
|
|
|
|
|
* // javascript
|
|
|
|
|
* makeMagicList(document.getElementById("list"), "li");
|
|
|
|
|
* ```
|
|
|
|
|
*
|
|
|
|
|
* @param {HTMLElement} container The parent container to use as a list.
|
|
|
|
|
* @param {string} itemSelector The selector name of list item elements.
|
|
|
|
|
* @param {Function} callback A function to call after each reordering.
|
|
|
|
|
*/
|
|
|
|
|
export function makeMagicList(container, itemSelector, callback) {
|
|
|
|
|
if (!container)
|
|
|
|
|
throw new Error("container not provided");
|
|
|
|
|
if (!itemSelector)
|
|
|
|
|
throw new Error("itemSelector not provided");
|
|
|
|
|
|
|
|
|
|
container.querySelectorAll(itemSelector).forEach(item => {
|
|
|
|
|
item.draggable = true;
|
|
|
|
|
item.addEventListener("dragstart", () => { item.classList.add("moving") });
|
|
|
|
|
item.addEventListener("dragend", () => { item.classList.remove("moving") });
|
2024-08-31 15:55:45 +01:00
|
|
|
|
|
|
|
|
// dragging on inputs should take priority
|
2024-08-31 01:30:30 +01:00
|
|
|
item.querySelectorAll("input").forEach(el => {
|
|
|
|
|
el.addEventListener("mousedown", () => { item.draggable = false });
|
|
|
|
|
el.addEventListener("mouseup", () => { item.draggable = true });
|
|
|
|
|
el.addEventListener("dragstart", e => { e.stopPropagation() });
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var lastCursorY;
|
|
|
|
|
container.addEventListener("dragover", event => {
|
|
|
|
|
const dragging = container.querySelector(itemSelector + ".moving");
|
|
|
|
|
if (!dragging) return;
|
|
|
|
|
|
|
|
|
|
let cursorY = event.touches ? event.touches[0].clientY : event.clientY;
|
|
|
|
|
|
|
|
|
|
// don't bother processing if we haven't moved
|
|
|
|
|
if (lastCursorY === cursorY) return
|
|
|
|
|
lastCursorY = cursorY;
|
|
|
|
|
|
|
|
|
|
// get the element positioned ahead of the cursor
|
|
|
|
|
const notMoving = [...container.querySelectorAll(itemSelector + ":not(.moving)")];
|
|
|
|
|
const afterElement = notMoving.reduce((previous, current) => {
|
|
|
|
|
const box = current.getBoundingClientRect();
|
|
|
|
|
const offset = cursorY - box.top - box.height / 2;
|
|
|
|
|
if (offset < 0 && offset > previous.offset)
|
|
|
|
|
return { offset: offset, element: current };
|
|
|
|
|
return previous;
|
|
|
|
|
}, { offset: Number.NEGATIVE_INFINITY }).element;
|
|
|
|
|
|
|
|
|
|
if (afterElement) {
|
|
|
|
|
container.insertBefore(dragging, afterElement);
|
|
|
|
|
} else {
|
|
|
|
|
container.appendChild(dragging);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (callback) callback();
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-10-19 05:01:55 +01:00
|
|
|
|
|
|
|
|
export function hijackClickEvent(container, link) {
|
|
|
|
|
container.addEventListener('click', event => {
|
|
|
|
|
if (event.target.tagName.toLowerCase() === 'a') return;
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
link.dispatchEvent(new MouseEvent('click', {
|
|
|
|
|
bubbles: true,
|
|
|
|
|
cancelable: true,
|
|
|
|
|
view: window,
|
|
|
|
|
ctrlKey: event.ctrlKey,
|
|
|
|
|
metaKey: event.metaKey,
|
|
|
|
|
shiftKey: event.shiftKey,
|
|
|
|
|
altKey: event.altKey,
|
|
|
|
|
button: event.button,
|
|
|
|
|
}));
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-10-21 23:22:57 +01:00
|
|
|
|
|
|
|
|
document.addEventListener("readystatechange", () => {
|
|
|
|
|
const navbar = document.getElementById("navbar");
|
|
|
|
|
document.getElementById("toggle-nav").addEventListener("click", event => {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
navbar.classList.toggle("open");
|
|
|
|
|
})
|
|
|
|
|
});
|