diff options
| -rw-r--r-- | app/views/revisions/index.html.erb | 123 | ||||
| -rw-r--r-- | config/locales/de.yml | 3 | ||||
| -rw-r--r-- | config/locales/en.yml | 3 | ||||
| -rw-r--r-- | public/stylesheets/admin.css | 22 |
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 | } |
