summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/views/revisions/index.html.erb123
-rw-r--r--config/locales/de.yml3
-rw-r--r--config/locales/en.yml3
-rw-r--r--public/stylesheets/admin.css22
4 files changed, 96 insertions, 55 deletions
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 @@
18 </p> 18 </p>
19<% end %> 19<% end %>
20 20
21<% pages = (@pages || @node.pages.all).reverse %>
22
21<table id="revisions" class="admin_table revisions_table"> 23<table id="revisions" class="admin_table revisions_table">
22 <thead> 24 <thead>
23 <tr class="header"> 25 <tr class="header">
24 <th><%= t(".first") %></th>
25 <th><%= t(".last") %></th>
26 <th><%= t("admin.columns.rev") %></th> 26 <th><%= t("admin.columns.rev") %></th>
27 <th><%= t("admin.columns.title") %></th> 27 <th><%= t("admin.columns.title") %></th>
28 <th><%= t("admin.columns.editor") %></th> 28 <th><%= t("admin.columns.editor") %></th>
@@ -31,24 +31,27 @@
31 <th></th> 31 <th></th>
32 </tr> 32 </tr>
33 <tr class="diff_sticky_bar"> 33 <tr class="diff_sticky_bar">
34 <td colspan="8"> 34 <td colspan="6">
35 <%= button_to t(".diff_revisions"), diff_node_revisions_path(@node), 35 <%= form_tag diff_node_revisions_path(@node), :method => :post,
36 method: :post, 36 :id => "diff_form", :class => "button_to computation" do %>
37 params: { translation_locale: @translation_locale }, 37 <%= hidden_field_tag :translation_locale, @translation_locale %>
38 form: { id: 'diff_form', class: 'button_to computation' }, 38 <%= hidden_field_tag :start_revision, pages[1]&.revision %>
39 disabled: true %> 39 <%= hidden_field_tag :end_revision, pages[0]&.revision %>
40 <span id="diff_selection_label" data-against="<%= t(".against_word") %>" data-selected="<%= t(".selected_word") %>"></span> 40 <%= submit_tag t(".diff_revisions") %>
41 <label><%= radio_button_tag :view, 'inline', true %> <%= t("revisions.inline") %></label> 41 <span id="diff_selection_label" aria-live="polite">
42 <label><%= radio_button_tag :view, 'side_by_side', false %> <%= t("revisions.side_by_side") %></label> 42 <%= t(".diff_selection_html",
43 :from => tag.span(:id => "diff_from"),
44 :to => tag.span(:id => "diff_to")) %>
45 </span>
46 <label><%= radio_button_tag :view, 'inline', true %> <%= t("revisions.inline") %></label>
47 <label><%= radio_button_tag :view, 'side_by_side', false %> <%= t("revisions.side_by_side") %></label>
48 <% end %>
43 </td> 49 </td>
44 </tr> 50 </tr>
45 </thead> 51 </thead>
46 <tbody> 52 <tbody>
47 <% pages = (@pages || @node.pages.all).reverse %> 53 <% pages.each do |page| %>
48 <% pages.each_with_index do |page, index| %> 54 <tr data-revision="<%= page.revision %>" tabindex="0" aria-pressed="false">
49 <tr>
50 <td><%= radio_button_tag :start_revision, page.revision, index == 1 %></td>
51 <td><%= radio_button_tag :end_revision, page.revision, index == 0 %></td>
52 <td class="revision"><%= page.revision %></td> 55 <td class="revision"><%= page.revision %></td>
53 <td class="title"><%= page.translations.find_by(:locale => @translation_locale)&.title || "—" %></td> 56 <td class="title"><%= page.translations.find_by(:locale => @translation_locale)&.title || "—" %></td>
54 <td class="user"><%= page.editor.try(:login) %></td> 57 <td class="user"><%= page.editor.try(:login) %></td>
@@ -65,45 +68,63 @@
65</table> 68</table>
66 69
67<%= javascript_tag nonce: true do %> 70<%= javascript_tag nonce: true do %>
68 function update_diff_button_state() { 71 (function () {
69 var start = document.querySelector('input[name="start_revision"]:checked'); 72 var table = document.getElementById('revisions');
70 var end = document.querySelector('input[name="end_revision"]:checked'); 73 var form = document.getElementById('diff_form');
71 var valid = start && end && start.value !== end.value; 74 if (!table || !form) { return; }
72 document.querySelector('#diff_form button[type="submit"]').disabled = !valid;
73 75
74 var label = document.getElementById('diff_selection_label'); 76 var from_field = form.querySelector('input[name="start_revision"]');
75 if (start && end) { 77 var to_field = form.querySelector('input[name="end_revision"]');
76 label.textContent = start.value + ' ' + label.dataset.against + ' ' + end.value; 78 var from_label = document.getElementById('diff_from');
77 } else if (start || end) { 79 var to_label = document.getElementById('diff_to');
78 label.textContent = (start || end).value + ' ' + label.dataset.selected; 80 var readout = document.getElementById('diff_selection_label');
79 } else { 81 var submit = form.querySelector('input[type="submit"]');
80 label.textContent = ''; 82 var rows = table.querySelectorAll('tbody tr');
81 }
82 }
83 83
84 document.querySelectorAll('input[name="start_revision"], input[name="end_revision"]') 84 // Two-slot FIFO: the newest pick becomes the target and the previous
85 .forEach(function(radio) { radio.addEventListener('change', update_diff_button_state); }); 85 // target becomes the source, so two taps anywhere give any pair.
86 // Picking the current target is a no-op; picking the current source
87 // swaps them.
88 var stack = [from_field.value, to_field.value].filter(function (v) { return v !== ''; });
86 89
87 update_diff_button_state(); 90 function render() {
91 var from = stack[0] || '';
92 var to = stack[1] || '';
93 from_field.value = from;
94 to_field.value = to;
95 from_label.textContent = from;
96 to_label.textContent = to;
97 readout.hidden = !(from && to);
98 submit.disabled = !(from && to && from !== to);
88 99
89 document.getElementById('diff_form').addEventListener('submit', function(e) { 100 rows.forEach(function (row) {
90 var start = document.querySelector('input[name="start_revision"]:checked'); 101 var rev = row.dataset.revision;
91 var end = document.querySelector('input[name="end_revision"]:checked'); 102 row.classList.toggle('diff_source', rev === from);
92 var view = document.querySelector('input[name="view"]:checked'); 103 row.classList.toggle('diff_target', rev === to);
93 if (start) { 104 row.setAttribute('aria-pressed', (rev === from || rev === to) ? 'true' : 'false');
94 var s = document.createElement('input'); 105 });
95 s.type = 'hidden'; s.name = 'start_revision'; s.value = start.value;
96 this.appendChild(s);
97 }
98 if (end) {
99 var en = document.createElement('input');
100 en.type = 'hidden'; en.name = 'end_revision'; en.value = end.value;
101 this.appendChild(en);
102 } 106 }
103 if (view) { 107
104 var v = document.createElement('input'); 108 function pick(rev) {
105 v.type = 'hidden'; v.name = 'view'; v.value = view.value; 109 if (rev === stack[1]) { return; }
106 this.appendChild(v); 110 stack.push(rev);
111 if (stack.length > 2) { stack.shift(); }
112 render();
107 } 113 }
108 }); 114
115 rows.forEach(function (row) {
116 row.addEventListener('click', function (e) {
117 if (e.target.closest('a, button, input, label')) { return; }
118 pick(row.dataset.revision);
119 });
120 row.addEventListener('keydown', function (e) {
121 if (e.key === 'Enter' || e.key === ' ') {
122 e.preventDefault();
123 pick(row.dataset.revision);
124 }
125 });
126 });
127
128 render();
129 })();
109<% end %> 130<% end %>
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 3f2eb0ab..35f262cc 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -757,8 +757,7 @@ de:
757 first: "Von" 757 first: "Von"
758 last: "Bis" 758 last: "Bis"
759 diff_revisions: "Revisionen vergleichen" 759 diff_revisions: "Revisionen vergleichen"
760 against_word: "mit" 760 diff_selection_html: "Vergleicht %{from} mit %{to}"
761 selected_word: "ausgewählt"
762 show_link: "anzeigen" 761 show_link: "anzeigen"
763 restore_link: "wiederherstellen" 762 restore_link: "wiederherstellen"
764 confirm_restore: "Diese Revision wiederherstellen?" 763 confirm_restore: "Diese Revision wiederherstellen?"
diff --git a/config/locales/en.yml b/config/locales/en.yml
index e8428f69..75c762a3 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -725,8 +725,7 @@ en:
725 first: "First" 725 first: "First"
726 last: "Last" 726 last: "Last"
727 diff_revisions: "Diff revisions" 727 diff_revisions: "Diff revisions"
728 against_word: "against" 728 diff_selection_html: "Comparing %{to} against %{from}"
729 selected_word: "selected"
730 show_link: "show" 729 show_link: "show"
731 restore_link: "restore" 730 restore_link: "restore"
732 confirm_restore: "Restore this revision?" 731 confirm_restore: "Restore this revision?"
diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css
index d3f3afe6..8bbf7289 100644
--- a/public/stylesheets/admin.css
+++ b/public/stylesheets/admin.css
@@ -686,6 +686,28 @@ form.button_to svg {
686 border-bottom: 1px solid var(--hairline); 686 border-bottom: 1px solid var(--hairline);
687} 687}
688 688
689.revisions_table tbody tr {
690 cursor: pointer;
691}
692
693.revisions_table tbody tr.diff_source {
694 background-color: var(--diff-del-bg);
695}
696
697.revisions_table tbody tr.diff_target {
698 background-color: var(--diff-ins-bg);
699}
700
701#diff_selection_label #diff_from {
702 color: var(--diff-del-fg);
703 font-weight: bold;
704}
705
706#diff_selection_label #diff_to {
707 color: var(--diff-ins-fg);
708 font-weight: bold;
709}
710
689.row_primary { 711.row_primary {
690 font-weight: bold; 712 font-weight: bold;
691} 713}