forked from fixin.me/fixin.me
f1578fad75
Use CSS `visibility` instead of `display` for #show/#hide. Access data attributes through `dataset` property.
98 lines
3.4 KiB
JavaScript
98 lines
3.4 KiB
JavaScript
// Turbo Streams actions.
|
|
|
|
const FORM_CONTROLS = ["BUTTON", "FIELDSET", "INPUT", "OPTGROUP", "OPTION", "SELECT",
|
|
"TEXTAREA"]
|
|
|
|
Object.assign(Turbo.StreamElement.prototype, {
|
|
disableElement(element) {
|
|
if (FORM_CONTROLS.includes(element.tagName)) {
|
|
element.setAttribute("disabled", "disabled")
|
|
} else {
|
|
element.setAttribute("aria-disabled", "true")
|
|
// NOTE: tabindex should not be disabled?
|
|
element.setAttribute("tabindex", "-1")
|
|
}
|
|
},
|
|
|
|
enableElement(element) {
|
|
if (FORM_CONTROLS.includes(element.tagName)) {
|
|
element.removeAttribute("disabled")
|
|
} else {
|
|
element.removeAttribute("aria-disabled")
|
|
// Assume 'tabindex' is not used explicitly, so removing it is safe.
|
|
element.removeAttribute("tabindex")
|
|
}
|
|
},
|
|
|
|
showElement(element) { element.style.visibility = "visible" }
|
|
})
|
|
|
|
Object.assign(Turbo.StreamActions, {
|
|
disable() {
|
|
this.targetElements.forEach((e) => { this.disableElement(e) })
|
|
},
|
|
|
|
enable() {
|
|
this.targetElements.forEach((e) => { this.enableElement(e) })
|
|
},
|
|
|
|
hide() {
|
|
this.targetElements.forEach((e) => { e.style.visibility = "collapse" })
|
|
},
|
|
|
|
show() {
|
|
this.targetElements.forEach((e) => { this.showElement(e) })
|
|
},
|
|
|
|
unselect() {
|
|
this.targetElements.forEach((e) => { e.checked = false; this.enableElement(e) })
|
|
},
|
|
|
|
validate_form() {
|
|
this.targetElements.forEach((form) => {
|
|
this.templateContent.querySelectorAll("span").forEach((span) => {
|
|
var control = document.querySelector(
|
|
`[form="${form.id}"][name="${span.dataset.name}"]`
|
|
)
|
|
control.setCustomValidity(span.textContent)
|
|
// Add event listener in capture phase to clear custom validity before
|
|
// `[oninput]` or any other bubble phase handler is invoked.
|
|
// https://www.quirksmode.org/js/events_order.html
|
|
// TODO: keep invalidating current value whenever user re-enters it. Store
|
|
// invalid value and validation message in `[data-invalid-message/value]`
|
|
// and check input value on "input" event (below) (using `[pattern]` may not
|
|
// be suitable due to inability to customize validtion message; also
|
|
// pattern may be used for other, conflicting purposes).
|
|
// After user executes any other action (while form is open), remove `[data-]`
|
|
// attributes and custom validation, as value may no longer be invalid.
|
|
control.addEventListener(
|
|
"input",
|
|
function(event) { event.target.setCustomValidity("") },
|
|
{capture: true, once: true}
|
|
)
|
|
})
|
|
form.reportValidity()
|
|
})
|
|
},
|
|
|
|
close_form() {
|
|
this.targetElements.forEach((e) => {
|
|
// Move focus if there's no focus or focus inside form being closed.
|
|
const focused = document.activeElement
|
|
if (!focused || (focused == document.body) || e.contains(focused)) {
|
|
let nextForm = e.parentElement.querySelector(`#${e.id} ~ tr:has([autofocus])`)
|
|
nextForm ??= e.parentElement.querySelector("tr:has([autofocus])")
|
|
nextForm?.querySelector("[autofocus]").focus()
|
|
}
|
|
document.getElementById(e.dataset.form).remove()
|
|
if ('link' in e.dataset) {
|
|
this.enableElement(document.getElementById(e.dataset.link))
|
|
}
|
|
if ('hiddenRow' in e.dataset) {
|
|
this.showElement(document.getElementById(e.dataset.hiddenRow))
|
|
}
|
|
e.remove()
|
|
})
|
|
}
|
|
})
|