/** * A loading bar that hooks into htmx events to automatically indicate the * progression of pending requests. * * ```html *
* ``` * Only the first loader will be used. */ (function () { var MAX_INCREMENT = 20; var MIN_INCREMENT = 3; var ADVANCE_UNTIL = 90; var pending = 0; var increment = MAX_INCREMENT; var timeout = null; var interval = null; function getLoader() { return document.querySelector("[data-loader]"); } function clamp(x, a, b) { return Math.max(a, Math.min(x, b)); } function progress(loader) { loader.hidden = false; var current = parseInt(loader.dataset.loader); if (current < ADVANCE_UNTIL) { current = Math.min(current + increment, ADVANCE_UNTIL); loader.dataset.loader = current; loader.style.right = 100 - current + "%"; increment = clamp(Math.floor(increment * 0.65), MIN_INCREMENT, MAX_INCREMENT); } } function start() { pending++; if (pending > 1) { return; } clearTimeout(timeout); clearInterval(interval); increment = MAX_INCREMENT; var loader = getLoader(); if (!loader) { return; } // Give the request a reasonable time to complete before triggering the // loader. timeout = setTimeout(function () { loader.dataset.loader = 0; loader.style.right = "100%"; loader.hidden = false; // Increment the loader state until it reaches the threshold. // Trickery and lies! interval = setInterval(function () { progress(loader) }, 100); }, 250); } function finish() { pending--; if (pending <= 0) { clearTimeout(timeout); clearInterval(interval); var loader = getLoader(); if (!loader) { return; } loader.style.right = 0; timeout = setTimeout(function () { loader.hidden = true; timeout = setTimeout(function () { loader.dataset.loader = 0; loader.style.right = "100%"; }, 500); }, 500); return; } } addEventListener("htmx:beforeRequest", start); addEventListener("htmx:afterRequest", finish); })();