Skip to content

Instantly share code, notes, and snippets.

@tonysm
Last active September 19, 2023 02:34
Show Gist options
  • Save tonysm/730d64fbd921e8047a42dbcd393c90b4 to your computer and use it in GitHub Desktop.
Save tonysm/730d64fbd921e8047a42dbcd393c90b4 to your computer and use it in GitHub Desktop.
Bulk Form Stimulus Controller
/**
* Usage:
* <div
* data-controller="bulk-form"
* data-action="
* keydown@window->bulk-form#toggleShiftKey
* keyup@window->bulk-form#toggleShiftKey
* "
* >
* <form id="bulk_form" data-bulk-form-target="form"><!-- ... --></form>
*
* <div id="items">
* <div id="item_123">
* <input
* form="bulk_form"
* data-bulk-form-target="input"
* data-action="
* keydown.down->bulk-form#focusNext:prevent
* keydown.up->bulk-form#focusPrevious:prevent
* bulk-form#update
* "
* />
* </div>
* <div id="item_456"><!-- ... --></div>
* </div>
* </div>
*/
Stimulus.register("bulk-form", class extends Controller {
static targets = ["form", "input"]
isPressingShiftKey = false
previouslyChecked = null
connect() {
this.updateButtons()
}
update({ target }) {
this.updateButtons()
if (target.checked) {
if (this.isPressingShiftKey && this.previouslyChecked) {
this.shiftSelect(target, this.previouslyChecked)
}
this.previouslyChecked = target
}
}
toggleShiftKey({ key, shiftKey }) {
if (key === "Shift") {
this.isPressingShiftKey = Boolean(shiftKey)
}
}
focusNext({ target }) {
this.focusOn(this.inputTargets[this.inputTargets.findIndex(input => input === target) + 1] || target)
}
focusPrevious({ target }) {
this.focusOn(this.inputTargets[this.inputTargets.findIndex(input => input === target) - 1] || target)
}
// private
focusOn(element) {
element.focus()
}
shiftSelect(current, previous) {
let start = this.inputTargets.findIndex(input => input === current)
let end = this.inputTargets.findIndex(input => input === previous)
// If we can't find the previous element, it was probably from the DOM...
if (end === -1) return
this.inputTargets
.slice(Math.min(start, end), Math.max(start, end))
.forEach(input => input.checked = true)
}
updateButtons() {
if (this.hasCheckedInputs) {
this.enableButtons()
} else {
this.disableButtons()
}
}
inputTargetDisconnected() {
this.updateButtons()
}
enableButtons() {
this.buttons.forEach(btn => btn.removeAttribute('disabled'))
}
disableButtons() {
this.buttons.forEach(btn => btn.setAttribute('disabled', true))
}
get buttons() {
return Array.from(this.formTarget.querySelectorAll('[type=submit]'))
}
get hasCheckedInputs() {
return this.inputTargets.some(input => input.checked)
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment