forked from fixin.me/fixin.me
Add stimulus-rails gem and wire up 7 controllers: - measurements_view_controller: view toggle (compact/wide) via localStorage - measurements_controller: grouped rows MutationObserver - charts_controller: Plotly chart rendering - form_controller: keyboard shortcuts (Escape/Enter) and submit validation - details_controller: quantity picker state, focusout close, MutationObserver - readout_unit_controller: default unit button enable/disable + PATCH submission - drag_controller: drag-and-drop for quantity reparenting and unit rebasing Remove all inline onclick/onkeydown/ondrag*/onsubmit handlers from templates. Remove all window.* global exports from application.js. Remove bare <script> block from measurements/_form.html.erb. Remove turbo:load listeners for behavior now in controller connect(). application.js now only contains: Turbo Stream custom action definitions and the showPage visibility listener. Document Stimulus conventions in CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
75 lines
2.4 KiB
JavaScript
75 lines
2.4 KiB
JavaScript
// Configure your import map in config/importmap.rb. Read more:
|
|
// https://github.com/rails/importmap-rails
|
|
import "@hotwired/turbo-rails"
|
|
import "controllers"
|
|
|
|
|
|
/* Hide page before loaded for testing purposes */
|
|
function showPage() {
|
|
document.documentElement.style.visibility = "visible"
|
|
}
|
|
document.addEventListener('turbo:load', showPage)
|
|
|
|
|
|
/* Turbo stream actions */
|
|
Turbo.StreamElement.prototype.disableElement = function(element) {
|
|
element.setAttribute("disabled", "disabled")
|
|
element.setAttribute("aria-disabled", "true")
|
|
element.setAttribute("tabindex", "-1")
|
|
}
|
|
Turbo.StreamActions.disable = function() {
|
|
this.targetElements.forEach((e) => { this.disableElement(e) })
|
|
}
|
|
|
|
Turbo.StreamElement.prototype.enableElement = function(element) {
|
|
element.removeAttribute("disabled")
|
|
element.removeAttribute("aria-disabled")
|
|
// Assume 'tabindex' is not used explicitly, so removing it is safe
|
|
element.removeAttribute("tabindex")
|
|
}
|
|
Turbo.StreamActions.enable = function() {
|
|
this.targetElements.forEach((e) => { this.enableElement(e) })
|
|
}
|
|
|
|
/* TODO: change to visibility = collapse to avoid width change? */
|
|
Turbo.StreamActions.hide = function() {
|
|
this.targetElements.forEach((e) => { e.style.display = "none" })
|
|
}
|
|
|
|
Turbo.StreamActions.show = function() {
|
|
this.targetElements.forEach((e) => { e.style.removeProperty("display") })
|
|
}
|
|
|
|
/*
|
|
Turbo.StreamActions.collapse = function() {
|
|
this.targetElements.forEach((e) => { e.style.visibility = "collapse" })
|
|
}
|
|
*/
|
|
|
|
Turbo.StreamActions.close_form = function() {
|
|
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.getAttribute("data-form")).remove()
|
|
if (e.hasAttribute("data-link")) {
|
|
this.enableElement(document.getElementById(e.getAttribute("data-link")))
|
|
}
|
|
if (e.hasAttribute("data-hidden-row")) {
|
|
document.getElementById(e.getAttribute("data-hidden-row")).removeAttribute("style")
|
|
}
|
|
e.remove()
|
|
})
|
|
}
|
|
|
|
Turbo.StreamActions.unselect = function() {
|
|
this.targetElements.forEach((e) => {
|
|
e.checked = false
|
|
this.enableElement(e)
|
|
})
|
|
}
|