generated from boboko/starter
43 lines
1.4 KiB
JavaScript
43 lines
1.4 KiB
JavaScript
import { Controller } from '@hotwired/stimulus'
|
|
|
|
// Heart toggle (x-wishlist-button). Posts the form with fetch and reflects the
|
|
// server's answer on aria-pressed, which the CSS uses to swap the outline and
|
|
// filled heart. If the request fails, falls back to a normal form submit.
|
|
export default class extends Controller {
|
|
static targets = ['button', 'status']
|
|
|
|
static values = {
|
|
addLabel: String,
|
|
removeLabel: String,
|
|
addedMessage: String,
|
|
removedMessage: String,
|
|
}
|
|
|
|
async toggle(event) {
|
|
event.preventDefault()
|
|
|
|
if (this.busy) return
|
|
this.busy = true
|
|
|
|
try {
|
|
const response = await fetch(this.element.action, {
|
|
method: 'POST',
|
|
headers: { Accept: 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
|
|
body: new FormData(this.element),
|
|
})
|
|
|
|
if (!response.ok) throw new Error(`Wishlist toggle failed: ${response.status}`)
|
|
|
|
const { active } = await response.json()
|
|
|
|
this.buttonTarget.setAttribute('aria-pressed', active ? 'true' : 'false')
|
|
this.buttonTarget.setAttribute('aria-label', active ? this.removeLabelValue : this.addLabelValue)
|
|
this.statusTarget.textContent = active ? this.addedMessageValue : this.removedMessageValue
|
|
} catch {
|
|
this.element.submit()
|
|
} finally {
|
|
this.busy = false
|
|
}
|
|
}
|
|
}
|