import { Controller } from '@hotwired/stimulus' export default class extends Controller { static targets = ['star', 'input', 'error'] static values = { rating: { type: Number, default: 0 } } connect() { this.#fill(this.ratingValue) } // Bound to the form's submit event (this controller sits on the
// itself, not just the star widget) — a plain `required` on the hidden // rating input would never surface: browsers exclude type="hidden" from // constraint validation entirely, so there'd be nothing to see or hear. validate(event) { if (this.ratingValue < 1) { event.preventDefault() this.errorTarget.hidden = false this.starTargets[0]?.focus() } } hover(event) { this.#fill(parseInt(event.currentTarget.dataset.value)) } leave() { this.#fill(this.ratingValue) } select(event) { const val = parseInt(event.currentTarget.dataset.value) this.ratingValue = val this.inputTarget.value = val this.errorTarget.hidden = true this.starTargets.forEach(star => { star.setAttribute('aria-pressed', String(parseInt(star.dataset.value) === val)) }) } #fill(upTo) { this.starTargets.forEach(star => { const filled = parseInt(star.dataset.value) <= upTo star.querySelector('svg').setAttribute('fill', filled ? 'currentColor' : 'none') }) } }