Skip to content

Instantly share code, notes, and snippets.

@tortuetorche
Forked from rainerborene/dom.js
Created March 15, 2021 12:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tortuetorche/8001c9417baf405633bff9de17b0d0e8 to your computer and use it in GitHub Desktop.
Save tortuetorche/8001c9417baf405633bff9de17b0d0e8 to your computer and use it in GitHub Desktop.
rails-ujs is a past thing. Here is how you can accomplish almost the same behaviour with Stimulus and Hotwire.
export function reduceToParams(elements) {
return elements.reduce((hash, node) => Object.assign(hash, { [node.name]: node.value }), {})
}
export function stopEverything(e) {
e.preventDefault()
e.stopPropagation()
e.stopImmediatePropagation()
}
import ApplicationController from "controllers/application_controller"
import { useMeta } from "stimulus-use"
import { stopEverything } from "helpers/dom"
import request from "helpers/request"
import RemoteForm from "helpers/remote_form"
import { reduceToParams } from "helpers/dom"
import { toggleSubmitDisabled } from "helpers/turbo"
export default class extends ApplicationController {
static values = { url: String, method: String, target: String }
static metaNames = ["csrf-param", "csrf-token"]
static targets = ["input"]
connect() {
useMeta(this)
}
// Actions
async submit(event) {
if (event) stopEverything(event)
const form = new RemoteForm({
action: this.urlValue,
method: this.methodValue,
target: this.targetValue,
params: reduceToParams(this.inputTargets)
})
form.connect()
toggleSubmitDisabled(this.element, true)
await form.submit()
toggleSubmitDisabled(this.element, false)
this.dispatch("success")
form.disconnect()
}
}
export default class RemoteForm {
constructor(options) {
this.options = options
this.options.params = this.options.params ?? {}
this.element = document.createElement("form")
this.element.action = this.action
this.element.method = this.method
this.element.target = this.target
this.element.style.display = "none"
}
// Actions
connect() {
document.body.appendChild(this.element)
}
disconnect() {
this.element.remove()
}
submit() {
const scrollTop = document.scrollingElement.scrollTop
this.reset()
for (const [key ,value] of Object.entries(this.params)) {
this.append(key, value)
}
return new Promise(resolve => {
this.element.requestSubmit()
if (this.turboDisabled) {
return resolve()
}
this.element.addEventListener("turbo:submit-end", () => {
document.scrollingElement.scrollTo(0, scrollTop)
resolve()
}, { once: true })
})
}
set(name, value) {
this.options.params[name] = value
return this
}
// Private
get turboDisabled() {
return this.element.closest("[data-turbo=false]") !== null
}
get action() {
return this.options.action
}
get method() {
return this.options.method === "get" ? "get" : "post"
}
get target() {
return this.options.target
}
get csrfParamName() {
return getMeta("csrf-param")
}
get csrfToken() {
return getMeta("csrf-token")
}
get railsParams() {
if (this.options.method === "get") {
return {}
} else {
return { _method: this.options.method, [this.csrfParamName]: this.csrfToken }
}
}
get params() {
return Object.assign({}, this.railsParams, this.options.params)
}
get inputs() {
return [...this.element.querySelectorAll("input[type=hidden]")]
}
reset() {
this.inputs.forEach(input => input.remove())
}
append(name, value) {
const input = document.createElement("input")
input.type = "hidden"
input.name = name
input.value = value
this.element.appendChild(input)
}
}
function getMeta(name) {
const meta = document.querySelector(`meta[name=${name}]`)
return meta && meta.content
}
import { navigator } from "@hotwired/turbo"
const { adapter } = navigator.delegate
export function showProgressBar() {
adapter.progressBar.setValue(0)
adapter.progressBar.show()
}
export function hideProgressBar() {
adapter.progressBar.setValue(1)
adapter.progressBar.hide()
}
export function setDisableWithText(element) {
if (element instanceof HTMLButtonElement) {
element.olderInnerHTML = element.innerHTML
element.innerHTML = element.dataset.disableWith
} else if (element instanceof HTMLInputElement) {
element.olderValue = element.value
element.value = element.dataset.disableWith
}
}
export function undoDisableWithText(element) {
if (element instanceof HTMLButtonElement) {
element.innerHTML = element.olderInnerHTML
} else if (element instanceof HTMLInputElement) {
element.value = element.olderValue
}
}
export function enableSubmits(submits) {
submits.forEach(element => {
if (element.dataset.disableWith) {
undoDisableWithText(element)
}
element.disabled = false
})
}
export function disableSubmits(submits) {
submits.forEach(element => {
if (element.dataset.disableWith) {
setDisableWithText(element)
}
element.disabled = true
})
}
export function toggleSubmitDisabled(form, toggle) {
const submits = [...form.querySelectorAll("button[type=submit], input[type=submit]")]
if (toggle) {
disableSubmits(submits)
} else {
enableSubmits(submits)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment