aboutsummaryrefslogtreecommitdiff
path: root/static/js/ext/loading-states.js
diff options
context:
space:
mode:
authorAlexander Neonxp Kiryukhin <i@neonxp.ru>2024-10-12 02:52:22 +0300
committerAlexander Neonxp Kiryukhin <i@neonxp.ru>2024-10-12 02:53:52 +0300
commitd05ea66f4bbcf0cc5c8908f3435c68de1b070fa1 (patch)
tree7c7a769206646f2b81a0eda0680f0be5033a4197 /static/js/ext/loading-states.js
Начальная версияv0.0.1
Diffstat (limited to 'static/js/ext/loading-states.js')
-rw-r--r--static/js/ext/loading-states.js189
1 files changed, 189 insertions, 0 deletions
diff --git a/static/js/ext/loading-states.js b/static/js/ext/loading-states.js
new file mode 100644
index 0000000..1774304
--- /dev/null
+++ b/static/js/ext/loading-states.js
@@ -0,0 +1,189 @@
+;(function () {
+
+ if (htmx.version && !htmx.version.startsWith("1.")) {
+ console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
+ ". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
+ }
+
+ let loadingStatesUndoQueue = []
+
+ function loadingStateContainer(target) {
+ return htmx.closest(target, '[data-loading-states]') || document.body
+ }
+
+ function mayProcessUndoCallback(target, callback) {
+ if (document.body.contains(target)) {
+ callback()
+ }
+ }
+
+ function mayProcessLoadingStateByPath(elt, requestPath) {
+ const pathElt = htmx.closest(elt, '[data-loading-path]')
+ if (!pathElt) {
+ return true
+ }
+
+ return pathElt.getAttribute('data-loading-path') === requestPath
+ }
+
+ function queueLoadingState(sourceElt, targetElt, doCallback, undoCallback) {
+ const delayElt = htmx.closest(sourceElt, '[data-loading-delay]')
+ if (delayElt) {
+ const delayInMilliseconds =
+ delayElt.getAttribute('data-loading-delay') || 200
+ const timeout = setTimeout(function () {
+ doCallback()
+
+ loadingStatesUndoQueue.push(function () {
+ mayProcessUndoCallback(targetElt, undoCallback)
+ })
+ }, delayInMilliseconds)
+
+ loadingStatesUndoQueue.push(function () {
+ mayProcessUndoCallback(targetElt, function () { clearTimeout(timeout) })
+ })
+ } else {
+ doCallback()
+ loadingStatesUndoQueue.push(function () {
+ mayProcessUndoCallback(targetElt, undoCallback)
+ })
+ }
+ }
+
+ function getLoadingStateElts(loadingScope, type, path) {
+ return Array.from(htmx.findAll(loadingScope, "[" + type + "]")).filter(
+ function (elt) { return mayProcessLoadingStateByPath(elt, path) }
+ )
+ }
+
+ function getLoadingTarget(elt) {
+ if (elt.getAttribute('data-loading-target')) {
+ return Array.from(
+ htmx.findAll(elt.getAttribute('data-loading-target'))
+ )
+ }
+ return [elt]
+ }
+
+ htmx.defineExtension('loading-states', {
+ onEvent: function (name, evt) {
+ if (name === 'htmx:beforeRequest') {
+ const container = loadingStateContainer(evt.target)
+
+ const loadingStateTypes = [
+ 'data-loading',
+ 'data-loading-class',
+ 'data-loading-class-remove',
+ 'data-loading-disable',
+ 'data-loading-aria-busy',
+ ]
+
+ let loadingStateEltsByType = {}
+
+ loadingStateTypes.forEach(function (type) {
+ loadingStateEltsByType[type] = getLoadingStateElts(
+ container,
+ type,
+ evt.detail.pathInfo.requestPath
+ )
+ })
+
+ loadingStateEltsByType['data-loading'].forEach(function (sourceElt) {
+ getLoadingTarget(sourceElt).forEach(function (targetElt) {
+ queueLoadingState(
+ sourceElt,
+ targetElt,
+ function () {
+ targetElt.style.display =
+ sourceElt.getAttribute('data-loading') ||
+ 'inline-block' },
+ function () { targetElt.style.display = 'none' }
+ )
+ })
+ })
+
+ loadingStateEltsByType['data-loading-class'].forEach(
+ function (sourceElt) {
+ const classNames = sourceElt
+ .getAttribute('data-loading-class')
+ .split(' ')
+
+ getLoadingTarget(sourceElt).forEach(function (targetElt) {
+ queueLoadingState(
+ sourceElt,
+ targetElt,
+ function () {
+ classNames.forEach(function (className) {
+ targetElt.classList.add(className)
+ })
+ },
+ function() {
+ classNames.forEach(function (className) {
+ targetElt.classList.remove(className)
+ })
+ }
+ )
+ })
+ }
+ )
+
+ loadingStateEltsByType['data-loading-class-remove'].forEach(
+ function (sourceElt) {
+ const classNames = sourceElt
+ .getAttribute('data-loading-class-remove')
+ .split(' ')
+
+ getLoadingTarget(sourceElt).forEach(function (targetElt) {
+ queueLoadingState(
+ sourceElt,
+ targetElt,
+ function () {
+ classNames.forEach(function (className) {
+ targetElt.classList.remove(className)
+ })
+ },
+ function() {
+ classNames.forEach(function (className) {
+ targetElt.classList.add(className)
+ })
+ }
+ )
+ })
+ }
+ )
+
+ loadingStateEltsByType['data-loading-disable'].forEach(
+ function (sourceElt) {
+ getLoadingTarget(sourceElt).forEach(function (targetElt) {
+ queueLoadingState(
+ sourceElt,
+ targetElt,
+ function() { targetElt.disabled = true },
+ function() { targetElt.disabled = false }
+ )
+ })
+ }
+ )
+
+ loadingStateEltsByType['data-loading-aria-busy'].forEach(
+ function (sourceElt) {
+ getLoadingTarget(sourceElt).forEach(function (targetElt) {
+ queueLoadingState(
+ sourceElt,
+ targetElt,
+ function () { targetElt.setAttribute("aria-busy", "true") },
+ function () { targetElt.removeAttribute("aria-busy") }
+ )
+ })
+ }
+ )
+ }
+
+ if (name === 'htmx:beforeOnLoad') {
+ while (loadingStatesUndoQueue.length > 0) {
+ loadingStatesUndoQueue.shift()()
+ }
+ }
+ },
+ })
+})()