184 lines
5.0 KiB
JavaScript
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()()
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
})
|
||
|
})()
|