forked from fixin.me/fixin.me
Migrate all inline JS to Stimulus controllers
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>
This commit is contained in:
@@ -1,2 +1,4 @@
|
||||
<div class="main-area" id="measurements-charts"></div>
|
||||
<script id="charts-data" type="application/json"><%= raw @readouts_json %></script>
|
||||
<div data-controller="charts">
|
||||
<div class="main-area" id="measurements-charts" data-charts-target="container"></div>
|
||||
<script id="charts-data" type="application/json" data-charts-target="data"><%= raw @readouts_json %></script>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<%= tabular_fields_for @readout, form: form_tag do |form| %>
|
||||
<%- tag.tr id: row, class: "form", onkeydown: "formProcessKey(event)",
|
||||
data: {form: form_tag, hidden_row: hidden_row, link: link} do %>
|
||||
<%- tag.tr id: row, class: "form",
|
||||
data: {controller: 'form', action: 'keydown->form#processKey',
|
||||
form: form_tag, hidden_row: hidden_row, link: link} do %>
|
||||
<td><%= @readout.quantity %></td>
|
||||
<td class="ralign">
|
||||
<%= form.number_field :value, required: true, autofocus: true %>
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
id: form_tag do |form| %>
|
||||
<table class="items-table">
|
||||
<tbody>
|
||||
<%= tag.tr id: row, class: "form", onkeydown: "formProcessKey(event)",
|
||||
data: {form: form_tag, hidden_row: hidden_row} do %>
|
||||
<%= tag.tr id: row, class: "form",
|
||||
data: {controller: 'form', action: 'keydown->form#processKey',
|
||||
form: form_tag, hidden_row: hidden_row} do %>
|
||||
<td><%= @readout.quantity %></td>
|
||||
<td class="ralign">
|
||||
<%= form.number_field :value, required: true, autofocus: true %>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<%= tabular_form_with model: Measurement.new, id: :measurement_form,
|
||||
class: 'topside-area flex vertical center',
|
||||
html: {onkeydown: 'formProcessKey(event)'} do |form| %>
|
||||
html: {data: {controller: 'form', action: 'keydown->form#processKey'}} do |form| %>
|
||||
|
||||
<table class="items-table center">
|
||||
<tbody id="readouts">
|
||||
@@ -17,17 +17,18 @@
|
||||
|
||||
<%# TODO: right-click selection; unnecessary with hierarchical tags? %>
|
||||
<details id="quantity_select" class="center hexpand" open
|
||||
onkeydown="detailsProcessKey(event)">
|
||||
data-controller="details"
|
||||
data-action="focusout->details#close change->details#change keydown->details#processKey">
|
||||
<summary autofocus>
|
||||
<!-- TODO: Set content with CSS when span empty to avoid duplication -->
|
||||
<span data-prompt="<%= t('.select_quantity') %>">
|
||||
<span data-prompt="<%= t('.select_quantity') %>" data-details-target="countLabel">
|
||||
<%= t('.select_quantity') %>
|
||||
</span>
|
||||
<%= image_button_tag t(:apply), "update", name: nil, disabled: true,
|
||||
formaction: new_readout_path, formmethod: :get, formnovalidate: true,
|
||||
data: {turbo_stream: true} %>
|
||||
data: {turbo_stream: true, details_target: 'submitButton'} %>
|
||||
</summary>
|
||||
<ul><%= quantities_check_boxes(@quantities) %></ul>
|
||||
<ul data-details-target="list"><%= quantities_check_boxes(@quantities) %></ul>
|
||||
</details>
|
||||
|
||||
<div class="flex reverse">
|
||||
@@ -36,10 +37,3 @@
|
||||
class: 'dangerous', onclick: render_turbo_stream('form_close') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<script>
|
||||
quantity_select.addEventListener('focusout', detailsClose)
|
||||
quantity_select.addEventListener('change', detailsChange)
|
||||
detailsObserver.observe(quantity_select.querySelector('ul'),
|
||||
{subtree: true, attributeFilter: ['disabled']})
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<%# TODO: show hint when no quantities/units defined %>
|
||||
<div class="rightside-area buttongrid">
|
||||
<div class="rightside-area buttongrid" data-controller="measurements-view">
|
||||
<% if current_user.at_least(:active) %>
|
||||
<%= image_link_to t('.new_measurement'), 'plus-outline', new_measurement_path,
|
||||
id: :new_measurement_link, onclick: 'this.blur();',
|
||||
@@ -7,16 +7,17 @@
|
||||
<% end %>
|
||||
<%= image_button_tag '', 'view-rows', name: nil, type: 'button',
|
||||
class: 'view-toggle', title: t('.view_compact'),
|
||||
data: {view: 'compact'}, onclick: "setMeasurementsView('compact')" %>
|
||||
data: {view: 'compact', action: 'click->measurements-view#set',
|
||||
'measurements-view-name-param': 'compact'} %>
|
||||
<%= image_button_tag '', 'view-columns', name: nil, type: 'button',
|
||||
class: 'view-toggle', title: t('.view_wide'),
|
||||
data: {view: 'wide'}, onclick: "setMeasurementsView('wide')" %>
|
||||
|
||||
data: {view: 'wide', action: 'click->measurements-view#set',
|
||||
'measurements-view-name-param': 'wide'} %>
|
||||
</div>
|
||||
|
||||
<div class="main-area measurements-section">
|
||||
<%= tag.div id: :measurement_edit_form %>
|
||||
<table class="items-table measurements-compact">
|
||||
<table class="items-table measurements-compact" data-controller="measurements">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= Quantity.model_name.human %></th>
|
||||
@@ -29,7 +30,7 @@
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="measurements">
|
||||
<tbody id="measurements" data-measurements-target="tbody">
|
||||
<%= render(partial: 'readout', collection: @measurements, as: :readout) || render_no_items %>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -38,4 +39,3 @@
|
||||
<%= render 'wide_table', wide_groups: @wide_groups, wide_quantities: @wide_quantities %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<%= tabular_fields_for @quantity, form: form_tag do |form| %>
|
||||
<%- tag.tr id: row, class: "form", onkeydown: "formProcessKey(event)",
|
||||
data: {link: link, form: form_tag, hidden_row: hidden_row} do %>
|
||||
<%- tag.tr id: row, class: "form",
|
||||
data: {controller: 'form', action: 'keydown->form#processKey',
|
||||
link: link, form: form_tag, hidden_row: hidden_row} do %>
|
||||
|
||||
<td style="--depth:<%= @quantity.depth %>">
|
||||
<%= form.text_field :name, required: true, autofocus: true, size: 20 %>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<%= tag.tr id: dom_id(quantity),
|
||||
ondragstart: "dragStart(event)", ondragend: "dragEnd(event)",
|
||||
ondragover: "dragOver(event)", ondrop: "drop(event)",
|
||||
ondragenter: "dragEnter(event)", ondragleave: "dragLeave(event)",
|
||||
data: {drag_path: reparent_quantity_path(quantity), drop_id: dom_id(quantity),
|
||||
drop_id_param: "quantity[parent_id]"} do %>
|
||||
draggable: true,
|
||||
data: {controller: 'drag',
|
||||
action: 'dragstart->drag#start dragend->drag#end dragover->drag#over drop->drag#drop dragenter->drag#enter dragleave->drag#leave',
|
||||
drag_drag_path_value: reparent_quantity_path(quantity),
|
||||
drag_drop_id_value: dom_id(quantity),
|
||||
drag_drop_id_param_value: 'quantity[parent_id]'} do %>
|
||||
|
||||
<td style="--depth:<%= quantity.depth %>">
|
||||
<%= link_to quantity, edit_quantity_path(quantity), class: 'link',
|
||||
|
||||
@@ -23,9 +23,10 @@
|
||||
<% end %>
|
||||
</tr>
|
||||
<%= tag.tr id: "quantity_", hidden: true,
|
||||
ondragover: "dragOver(event)", ondrop: "drop(event)",
|
||||
ondragenter: "dragEnter(event)", ondragleave: "dragLeave(event)",
|
||||
data: {drop_id: "quantity_", drop_id_param: "quantity[parent_id]"} do %>
|
||||
data: {controller: 'drag',
|
||||
action: 'dragover->drag#over drop->drag#drop dragenter->drag#enter dragleave->drag#leave',
|
||||
drag_drop_id_value: 'quantity_',
|
||||
drag_drop_id_param_value: 'quantity[parent_id]'} do %>
|
||||
<th colspan="5"><%= t '.top_level_drop' %></th>
|
||||
<% end %>
|
||||
</thead>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<%# TODO: add readout reordering by dragging %>
|
||||
<%= tabular_fields_for 'readouts[]', readout do |form| %>
|
||||
<%- tag.tr id: dom_id(readout.quantity, :new, :readout) do %>
|
||||
<%- tag.tr id: dom_id(readout.quantity, :new, :readout),
|
||||
data: {controller: 'readout-unit'} do %>
|
||||
<td>
|
||||
<%# TODO: add grayed readout index (in separate column?) %>
|
||||
<%= readout.quantity.relative_pathname(@superquantity) %>
|
||||
@@ -13,16 +13,18 @@
|
||||
<%= form.collection_select :unit_id, @user_units, :id,
|
||||
->(u){ sanitize(' ' * (u.base_id ? 1 : 0) + u.symbol) },
|
||||
{prompt: '', disabled: '', selected: readout.quantity.default_unit_id || ''}, required: true,
|
||||
data: {default_unit_id: readout.quantity.default_unit_id || ''},
|
||||
onchange: "readoutUnitChanged(this)" %>
|
||||
data: {default_unit_id: readout.quantity.default_unit_id || '',
|
||||
readout_unit_target: 'select',
|
||||
action: 'change->readout-unit#unitChanged'} %>
|
||||
</td>
|
||||
<td class="flex">
|
||||
<%# TODO: change to _link_ after giving up displaying relative paths %>
|
||||
<%= image_button_tag '', 'check-circle-outline',
|
||||
class: 'set-default-unit', name: nil, type: 'button', disabled: true,
|
||||
title: t('readouts.form.set_default_unit'),
|
||||
data: {path: quantity_path(readout.quantity)},
|
||||
onclick: 'setDefaultUnit(this)' %>
|
||||
data: {path: quantity_path(readout.quantity),
|
||||
readout_unit_target: 'button',
|
||||
action: 'click->readout-unit#setDefault'} %>
|
||||
<%= image_button_tag '', 'delete-outline', class: 'dangerous', name: nil,
|
||||
formaction: discard_readouts_path(readout.quantity),
|
||||
formmethod: :get, formnovalidate: true, data: {turbo_stream: true} %>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<%= tabular_fields_for @unit, form: form_tag do |form| %>
|
||||
<%- tag.tr id: row, class: "form", onkeydown: "formProcessKey(event)",
|
||||
data: {link: link, form: form_tag, hidden_row: hidden_row} do %>
|
||||
<%- tag.tr id: row, class: "form",
|
||||
data: {controller: 'form', action: 'keydown->form#processKey',
|
||||
link: link, form: form_tag, hidden_row: hidden_row} do %>
|
||||
|
||||
<td style="--depth:<%= @unit.base_id? ? 1 : 0 %>">
|
||||
<%= form.text_field :symbol, required: true, autofocus: true, size: 12 %>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<%= tag.tr id: dom_id(unit),
|
||||
ondragstart: "dragStart(event)", ondragend: "dragEnd(event)",
|
||||
ondragover: "dragOver(event)", ondrop: "drop(event)",
|
||||
ondragenter: "dragEnter(event)", ondragleave: "dragLeave(event)",
|
||||
data: {drag_path: rebase_unit_path(unit),
|
||||
drop_id: dom_id(unit.base || unit),
|
||||
drop_id_param: "unit[base_id]"} do %>
|
||||
draggable: true,
|
||||
data: {controller: 'drag',
|
||||
action: 'dragstart->drag#start dragend->drag#end dragover->drag#over drop->drag#drop dragenter->drag#enter dragleave->drag#leave',
|
||||
drag_drag_path_value: rebase_unit_path(unit),
|
||||
drag_drop_id_value: dom_id(unit.base || unit),
|
||||
drag_drop_id_param_value: 'unit[base_id]'} do %>
|
||||
|
||||
<td style="--depth:<%= unit.base_id? ? 1 : 0 %>">
|
||||
<%= link_to unit, edit_unit_path(unit), class: 'link', onclick: 'this.blur();',
|
||||
|
||||
@@ -22,9 +22,10 @@
|
||||
<% end %>
|
||||
</tr>
|
||||
<%= tag.tr id: "unit_", hidden: true,
|
||||
ondragover: "dragOver(event)", ondrop: "drop(event)",
|
||||
ondragenter: "dragEnter(event)", ondragleave: "dragLeave(event)",
|
||||
data: {drop_id: "unit_", drop_id_param: "unit[base_id]"} do %>
|
||||
data: {controller: 'drag',
|
||||
action: 'dragover->drag#over drop->drag#drop dragenter->drag#enter dragleave->drag#leave',
|
||||
drag_drop_id_value: 'unit_',
|
||||
drag_drop_id_param_value: 'unit[base_id]'} do %>
|
||||
<th colspan="5"><%= t '.top_level_drop' %></th>
|
||||
<% end %>
|
||||
</thead>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<%= labeled_form_for resource, url: user_registration_path,
|
||||
html: {class: 'main-area', onsubmit: 'formValidate(event)'} do |f| %>
|
||||
html: {class: 'main-area', data: {controller: 'form', action: 'submit->form#validate'}} do |f| %>
|
||||
|
||||
<%= f.email_field :email, required: true, size: 30, autofocus: true,
|
||||
autocomplete: 'email' %>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<%= labeled_form_for resource, url: user_session_path,
|
||||
html: {class: 'main-area', onsubmit: 'formValidate(event)'} do |f| %>
|
||||
html: {class: 'main-area', data: {controller: 'form', action: 'submit->form#validate'}} do |f| %>
|
||||
|
||||
<%= f.email_field :email, required: true, size: 30, autofocus: true,
|
||||
autocomplete: 'email' %>
|
||||
|
||||
Reference in New Issue
Block a user