Files
myep/assets/controllers/loading_controller.js
T
Björn Fromme 83589ee677 feat: htmx powered requests, improved loading indicator
feat: loading indicator and htmx form submit
2026-03-16 11:59:10 +01:00

82 lines
2.3 KiB
JavaScript

import {Controller} from '@hotwired/stimulus'
export default class extends Controller {
static targets = ['indicator']
static classes = ['hidden']
connect() {
this.isVisible = false
this.debounceTimeout = null
// Bind event handlers to preserve context
this.boundHandleBeforeRequest = this.handleBeforeRequest.bind(this)
this.boundHandleAfterRequest = this.handleAfterRequest.bind(this)
// Listen to HTMX events
document.body.addEventListener('htmx:beforeRequest', this.boundHandleBeforeRequest)
document.body.addEventListener('htmx:afterRequest', this.boundHandleAfterRequest)
}
disconnect() {
// Clean up event listeners
document.body.removeEventListener('htmx:beforeRequest', this.boundHandleBeforeRequest)
document.body.removeEventListener('htmx:afterRequest', this.boundHandleAfterRequest)
// Clear any pending timeout
if (this.debounceTimeout) {
clearTimeout(this.debounceTimeout)
}
}
handleBeforeRequest(event) {
// Don't start a new debounce if already visible
if (this.isVisible) {
return
}
// Show indicator after 200ms delay for field refreshes
this.debounceTimeout = setTimeout(() => {
this.show()
}, 200)
}
handleAfterRequest(event) {
// Clear debounce timer if request completes before 200ms
if (this.debounceTimeout) {
clearTimeout(this.debounceTimeout)
this.debounceTimeout = null
}
// Check if response contains HX-Redirect header
const xhr = event.detail.xhr
const hxRedirect = xhr.getResponseHeader('HX-Redirect')
// Keep loading indicator visible if redirecting
if (hxRedirect) {
// Ensure indicator is visible during redirect
if (!this.isVisible) {
this.show()
}
return
}
// Hide indicator if it's visible
this.hide()
}
show() {
this.isVisible = true
this.indicatorTarget.classList.remove(this.hiddenClass)
}
hide() {
this.isVisible = false
this.indicatorTarget.classList.add(this.hiddenClass)
}
toggle() {
this.indicatorTarget.classList.remove(this.hiddenClass)
}
}