home to your local SPACEGIRL 馃挮
arimelody.space
1/**
2 * Creates a reorderable list from any `container` and viable list item selector.
3 *
4 * This function is absolute magic and I love it
5 *
6 * Example:
7 * ```html
8 * <ul id="list">
9 * <li>Item 1</li>
10 * <li>Item 2</li>
11 * <li>Item 3</li>
12 * </ul>
13 * ```
14 * ```js
15 * // javascript
16 * makeMagicList(document.getElementById("list"), "li");
17 * ```
18 *
19 * @param {HTMLElement} container The parent container to use as a list.
20 * @param {string} itemSelector The selector name of list item elements.
21 * @param {Function} callback A function to call after each reordering.
22 */
23export function makeMagicList(container, itemSelector, callback) {
24 if (!container)
25 throw new Error("container not provided");
26 if (!itemSelector)
27 throw new Error("itemSelector not provided");
28
29 container.querySelectorAll(itemSelector).forEach(item => {
30 item.draggable = true;
31 item.addEventListener("dragstart", () => { item.classList.add("moving") });
32 item.addEventListener("dragend", () => { item.classList.remove("moving") });
33
34 // dragging on inputs should take priority
35 item.querySelectorAll("input").forEach(el => {
36 el.addEventListener("mousedown", () => { item.draggable = false });
37 el.addEventListener("mouseup", () => { item.draggable = true });
38 el.addEventListener("dragstart", e => { e.stopPropagation() });
39 });
40 });
41
42 var lastCursorY;
43 container.addEventListener("dragover", event => {
44 const dragging = container.querySelector(itemSelector + ".moving");
45 if (!dragging) return;
46
47 let cursorY = event.touches ? event.touches[0].clientY : event.clientY;
48
49 // don't bother processing if we haven't moved
50 if (lastCursorY === cursorY) return
51 lastCursorY = cursorY;
52
53 // get the element positioned ahead of the cursor
54 const notMoving = [...container.querySelectorAll(itemSelector + ":not(.moving)")];
55 const afterElement = notMoving.reduce((previous, current) => {
56 const box = current.getBoundingClientRect();
57 const offset = cursorY - box.top - box.height / 2;
58 if (offset < 0 && offset > previous.offset)
59 return { offset: offset, element: current };
60 return previous;
61 }, { offset: Number.NEGATIVE_INFINITY }).element;
62
63 if (afterElement) {
64 container.insertBefore(dragging, afterElement);
65 } else {
66 container.appendChild(dragging);
67 }
68
69 if (callback) callback();
70 });
71}
72
73export function hijackClickEvent(container, link) {
74 container.addEventListener('click', event => {
75 if (event.target.tagName.toLowerCase() === 'a') return;
76 event.preventDefault();
77 link.dispatchEvent(new MouseEvent('click', {
78 bubbles: true,
79 cancelable: true,
80 view: window,
81 ctrlKey: event.ctrlKey,
82 metaKey: event.metaKey,
83 shiftKey: event.shiftKey,
84 altKey: event.altKey,
85 button: event.button,
86 }));
87 });
88}
89
90document.addEventListener("readystatechange", () => {
91 const navbar = document.getElementById("navbar");
92 document.getElementById("toggle-nav").addEventListener("click", event => {
93 event.preventDefault();
94 navbar.classList.toggle("open");
95 })
96});