From dcdc892d940adffced3d2bc272d813a47c7d4700 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Fri, 14 Aug 2026 15:55:13 +0200 Subject: Select revisions to compare by picking rows Two hidden fields hold the pair, picking a row pushes it as the target and the previous target becomes the source, so any pair takes two picks. Source and target carry the diff view's own colours, in the rows and in the sticky bar's readout. Rows are focusable and respond to Enter and Space, the readout is the accessible equivalent of the colour, so it is aria-live. --- app/views/revisions/index.html.erb | 123 ++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 51 deletions(-) (limited to 'app/views') diff --git a/app/views/revisions/index.html.erb b/app/views/revisions/index.html.erb index c913b2a9..e951016f 100644 --- a/app/views/revisions/index.html.erb +++ b/app/views/revisions/index.html.erb @@ -18,11 +18,11 @@

<% end %> +<% pages = (@pages || @node.pages.all).reverse %> + - - @@ -31,24 +31,27 @@ - - <% pages = (@pages || @node.pages.all).reverse %> - <% pages.each_with_index do |page, index| %> - - - + <% pages.each do |page| %> + @@ -65,45 +68,63 @@
<%= t(".first") %><%= t(".last") %> <%= t("admin.columns.rev") %> <%= t("admin.columns.title") %> <%= t("admin.columns.editor") %>
- <%= button_to t(".diff_revisions"), diff_node_revisions_path(@node), - method: :post, - params: { translation_locale: @translation_locale }, - form: { id: 'diff_form', class: 'button_to computation' }, - disabled: true %> - " data-selected="<%= t(".selected_word") %>"> - - + + <%= form_tag diff_node_revisions_path(@node), :method => :post, + :id => "diff_form", :class => "button_to computation" do %> + <%= hidden_field_tag :translation_locale, @translation_locale %> + <%= hidden_field_tag :start_revision, pages[1]&.revision %> + <%= hidden_field_tag :end_revision, pages[0]&.revision %> + <%= submit_tag t(".diff_revisions") %> + + <%= t(".diff_selection_html", + :from => tag.span(:id => "diff_from"), + :to => tag.span(:id => "diff_to")) %> + + + + <% end %>
<%= radio_button_tag :start_revision, page.revision, index == 1 %><%= radio_button_tag :end_revision, page.revision, index == 0 %>
<%= page.revision %> <%= page.translations.find_by(:locale => @translation_locale)&.title || "—" %> <%= page.editor.try(:login) %>
<%= javascript_tag nonce: true do %> - function update_diff_button_state() { - var start = document.querySelector('input[name="start_revision"]:checked'); - var end = document.querySelector('input[name="end_revision"]:checked'); - var valid = start && end && start.value !== end.value; - document.querySelector('#diff_form button[type="submit"]').disabled = !valid; + (function () { + var table = document.getElementById('revisions'); + var form = document.getElementById('diff_form'); + if (!table || !form) { return; } - var label = document.getElementById('diff_selection_label'); - if (start && end) { - label.textContent = start.value + ' ' + label.dataset.against + ' ' + end.value; - } else if (start || end) { - label.textContent = (start || end).value + ' ' + label.dataset.selected; - } else { - label.textContent = ''; - } - } + var from_field = form.querySelector('input[name="start_revision"]'); + var to_field = form.querySelector('input[name="end_revision"]'); + var from_label = document.getElementById('diff_from'); + var to_label = document.getElementById('diff_to'); + var readout = document.getElementById('diff_selection_label'); + var submit = form.querySelector('input[type="submit"]'); + var rows = table.querySelectorAll('tbody tr'); - document.querySelectorAll('input[name="start_revision"], input[name="end_revision"]') - .forEach(function(radio) { radio.addEventListener('change', update_diff_button_state); }); + // Two-slot FIFO: the newest pick becomes the target and the previous + // target becomes the source, so two taps anywhere give any pair. + // Picking the current target is a no-op; picking the current source + // swaps them. + var stack = [from_field.value, to_field.value].filter(function (v) { return v !== ''; }); - update_diff_button_state(); + function render() { + var from = stack[0] || ''; + var to = stack[1] || ''; + from_field.value = from; + to_field.value = to; + from_label.textContent = from; + to_label.textContent = to; + readout.hidden = !(from && to); + submit.disabled = !(from && to && from !== to); - document.getElementById('diff_form').addEventListener('submit', function(e) { - var start = document.querySelector('input[name="start_revision"]:checked'); - var end = document.querySelector('input[name="end_revision"]:checked'); - var view = document.querySelector('input[name="view"]:checked'); - if (start) { - var s = document.createElement('input'); - s.type = 'hidden'; s.name = 'start_revision'; s.value = start.value; - this.appendChild(s); - } - if (end) { - var en = document.createElement('input'); - en.type = 'hidden'; en.name = 'end_revision'; en.value = end.value; - this.appendChild(en); + rows.forEach(function (row) { + var rev = row.dataset.revision; + row.classList.toggle('diff_source', rev === from); + row.classList.toggle('diff_target', rev === to); + row.setAttribute('aria-pressed', (rev === from || rev === to) ? 'true' : 'false'); + }); } - if (view) { - var v = document.createElement('input'); - v.type = 'hidden'; v.name = 'view'; v.value = view.value; - this.appendChild(v); + + function pick(rev) { + if (rev === stack[1]) { return; } + stack.push(rev); + if (stack.length > 2) { stack.shift(); } + render(); } - }); + + rows.forEach(function (row) { + row.addEventListener('click', function (e) { + if (e.target.closest('a, button, input, label')) { return; } + pick(row.dataset.revision); + }); + row.addEventListener('keydown', function (e) { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + pick(row.dataset.revision); + } + }); + }); + + render(); + })(); <% end %> -- cgit v1.3