BrainMinder/assets/static/js/htmx/ext/loading-states.js
2024-08-22 10:13:16 +02:00

184 lines
5.0 KiB
JavaScript

;(function () {
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()()
}
}
},
})
})()