diff options
| -rw-r--r-- | app/views/custom/page_templates/public/chapter_detail.html.erb | 1 | ||||
| -rw-r--r-- | app/views/custom/partials/_chapter.html.erb | 37 | ||||
| -rw-r--r-- | app/views/nodes/edit.html.erb | 21 | ||||
| -rw-r--r-- | app/views/page_translations/edit.html.erb | 24 | ||||
| -rw-r--r-- | public/javascripts/admin_interface.js | 43 | ||||
| -rw-r--r-- | public/stylesheets/admin.css | 68 | ||||
| -rw-r--r-- | public/stylesheets/ccc.css | 17 |
7 files changed, 189 insertions, 22 deletions
diff --git a/app/views/custom/page_templates/public/chapter_detail.html.erb b/app/views/custom/page_templates/public/chapter_detail.html.erb index f47353c..c4c2598 100644 --- a/app/views/custom/page_templates/public/chapter_detail.html.erb +++ b/app/views/custom/page_templates/public/chapter_detail.html.erb | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | <div class="article" lang="<%= @page.effective_lang %>"> | 1 | <div class="article" lang="<%= @page.effective_lang %>"> |
| 2 | <h2 class="headline"><%= @page.title %></h2> | 2 | <h2 class="headline"><%= @page.title %></h2> |
| 3 | <%= headline_image %> | ||
| 3 | <% if @page.node.external_url.present? %> | 4 | <% if @page.node.external_url.present? %> |
| 4 | <div class="chapter_url"><%= link_to @page.node.external_url, @page.node.external_url, target: '_blank', rel: 'noopener' %></div> | 5 | <div class="chapter_url"><%= link_to @page.node.external_url, @page.node.external_url, target: '_blank', rel: 'noopener' %></div> |
| 5 | <% end %> | 6 | <% end %> |
diff --git a/app/views/custom/partials/_chapter.html.erb b/app/views/custom/partials/_chapter.html.erb index 805559b..47f3950 100644 --- a/app/views/custom/partials/_chapter.html.erb +++ b/app/views/custom/partials/_chapter.html.erb | |||
| @@ -1,19 +1,26 @@ | |||
| 1 | <div class="article_partial chapter_partial" lang="<%= page.effective_lang %>"> | 1 | <div class="article_partial chapter_partial" lang="<%= page.effective_lang %>"> |
| 2 | <h2 class="headline"><%= link_to_path page.title, page.node.unique_name %></h2> | 2 | <div class="chapter_partial_layout"> |
| 3 | <% if page.abstract.present? %> | 3 | <% if page.assets.images.any? %> |
| 4 | <div class="chapter_location"><%= page.abstract %></div> | 4 | <%= link_to_path image_tag(page.assets.images.first.upload.url(:thumb), :alt => ""), page.node.unique_name, class: "chapter_thumbnail" %> |
| 5 | <% end %> | 5 | <% end %> |
| 6 | <% if page.node.external_url.present? %> | 6 | <div class="chapter_partial_content"> |
| 7 | <div class="chapter_url"><%= link_to page.node.external_url, page.node.external_url, target: '_blank', rel: 'noopener' %></div> | 7 | <h2 class="headline"><%= link_to_path page.title, page.node.unique_name %></h2> |
| 8 | <% end %> | 8 | <% if page.abstract.present? %> |
| 9 | <% open_days = page.node.events.tagged_with('open-day').order(:start_time) %> | 9 | <div class="chapter_location"><%= page.abstract %></div> |
| 10 | <% if open_days.any? %> | ||
| 11 | <div class="chapter_open_days"> | ||
| 12 | <span class="open_days_label"><%= t(:open_days_label) %>: </span> | ||
| 13 | <% open_days.each do |event| %> | ||
| 14 | <span class="event_schedule"><%= event_schedule_text(event) %></span> | ||
| 15 | <% end %> | 10 | <% end %> |
| 11 | <% if page.node.external_url.present? %> | ||
| 12 | <div class="chapter_url"><%= link_to page.node.external_url, page.node.external_url, target: '_blank', rel: 'noopener' %></div> | ||
| 13 | <% end %> | ||
| 14 | <% open_days = page.node.events.tagged_with('open-day').order(:start_time) %> | ||
| 15 | <% if open_days.any? %> | ||
| 16 | <div class="chapter_open_days"> | ||
| 17 | <span class="open_days_label"><%= t(:open_days_label) %>: </span> | ||
| 18 | <% open_days.each do |event| %> | ||
| 19 | <span class="event_schedule"><%= event_schedule_text(event) %></span> | ||
| 20 | <% end %> | ||
| 21 | </div> | ||
| 22 | <% end %> | ||
| 23 | <p class="excerpt"><%= sanitize page.body %></p> | ||
| 16 | </div> | 24 | </div> |
| 17 | <% end %> | 25 | </div> |
| 18 | <p class="excerpt"><%= sanitize page.body %></p> | ||
| 19 | </div> | 26 | </div> |
diff --git a/app/views/nodes/edit.html.erb b/app/views/nodes/edit.html.erb index e0e51d8..9506995 100644 --- a/app/views/nodes/edit.html.erb +++ b/app/views/nodes/edit.html.erb | |||
| @@ -116,10 +116,27 @@ | |||
| 116 | <div class="node_content"><%= d.text_area :abstract %></div> | 116 | <div class="node_content"><%= d.text_area :abstract %></div> |
| 117 | 117 | ||
| 118 | <div class="node_description">Body</div> | 118 | <div class="node_description">Body</div> |
| 119 | <div class="node_content"><%= d.text_area :body, :class => 'with_editor' %></div> | 119 | <div class="body_toolbar_row"> |
| 120 | <button type="button" id="preview_toggle" class="view_toggle" aria-pressed="false" aria-label="Toggle live preview" title="Toggle live preview"> | ||
| 121 | <%= icon("layout-sidebar-right", library: "tabler", "aria-hidden": true) %> <span>Live preview</span> | ||
| 122 | </button> | ||
| 123 | <button type="button" id="preview_force_render" class="view_toggle" style="display:none" aria-label="Force preview render" title="Force preview render"> | ||
| 124 | <%= icon("refresh", library: "tabler", "aria-hidden": true) %> <span>Force refresh</span> | ||
| 125 | </button> | ||
| 126 | </div> | ||
| 127 | |||
| 128 | <div id="editor_and_preview"> | ||
| 129 | <div class="preview_content"> | ||
| 130 | <div class="node_content"><%= d.text_area :body, :class => 'with_editor' %></div> | ||
| 131 | </div> | ||
| 132 | <div id="preview_panel" style="display:none"> | ||
| 133 | <iframe id="live_preview_iframe" title="Live preview" data-src="<%= preview_page_path(@page, :locale => I18n.default_locale) %>"></iframe> | ||
| 134 | </div> | ||
| 135 | </div> | ||
| 136 | </div> | ||
| 120 | <% end %> | 137 | <% end %> |
| 121 | 138 | ||
| 122 | <div class="node_action_bar node_action_bar_save"> | 139 | <div class="node_action_bar"> |
| 123 | <%= f.submit 'Save Draft' %> | 140 | <%= f.submit 'Save Draft' %> |
| 124 | <%= f.submit 'Save + Unlock + Exit' %> | 141 | <%= f.submit 'Save + Unlock + Exit' %> |
| 125 | </div> | 142 | </div> |
diff --git a/app/views/page_translations/edit.html.erb b/app/views/page_translations/edit.html.erb index 981fb74..0f64d40 100644 --- a/app/views/page_translations/edit.html.erb +++ b/app/views/page_translations/edit.html.erb | |||
| @@ -20,8 +20,8 @@ | |||
| 20 | </div> | 20 | </div> |
| 21 | 21 | ||
| 22 | <div id="page_editor"> | 22 | <div id="page_editor"> |
| 23 | <%= form_with url: node_translation_path(@node, @locale), method: :patch, local: true, id: "translation_edit_form", | 23 | <%= form_with url: node_translation_path(@node, @locale), method: :patch, local: true, id: "translation_edit_form", |
| 24 | data: { autosave_url: autosave_node_translation_path(@node, @locale), show_url: node_path(@node) } do |f| %> | 24 | data: { autosave_url: autosave_node_translation_path(@node, @locale), show_url: node_path(@node) } do |f| %> |
| 25 | <div id="content"> | 25 | <div id="content"> |
| 26 | <div class="node_description">Title</div> | 26 | <div class="node_description">Title</div> |
| 27 | <div class="node_content"> | 27 | <div class="node_content"> |
| @@ -34,8 +34,24 @@ | |||
| 34 | </div> | 34 | </div> |
| 35 | 35 | ||
| 36 | <div class="node_description">Body</div> | 36 | <div class="node_description">Body</div> |
| 37 | <div class="node_content"> | 37 | <div class="body_toolbar_row"> |
| 38 | <%= text_area_tag "page[body]", @translation&.body, :class => 'with_editor' %> | 38 | <button type="button" id="preview_toggle" class="view_toggle" aria-pressed="false" aria-label="Toggle live preview" title="Toggle live preview"> |
| 39 | <%= icon("layout-sidebar-right", library: "tabler", "aria-hidden": true) %> <span>Live preview</span> | ||
| 40 | </button> | ||
| 41 | <button type="button" id="preview_force_render" class="view_toggle" style="display:none" aria-label="Force preview render" title="Force preview render"> | ||
| 42 | <%= icon("refresh", library: "tabler", "aria-hidden": true) %> <span>Force refresh</span> | ||
| 43 | </button> | ||
| 44 | </div> | ||
| 45 | |||
| 46 | <div id="editor_and_preview"> | ||
| 47 | <div class="preview_content"> | ||
| 48 | <div class="node_content"> | ||
| 49 | <%= text_area_tag "page[body]", @translation&.body, :class => 'with_editor' %> | ||
| 50 | </div> | ||
| 51 | </div> | ||
| 52 | <div id="preview_panel" style="display:none"> | ||
| 53 | <iframe id="live_preview_iframe" title="Live preview" data-src="<%= preview_page_path(@page, :locale => @locale) %>"></iframe> | ||
| 54 | </div> | ||
| 39 | </div> | 55 | </div> |
| 40 | </div> | 56 | </div> |
| 41 | <% end %> | 57 | <% end %> |
diff --git a/public/javascripts/admin_interface.js b/public/javascripts/admin_interface.js index e3d4ff1..9da5d28 100644 --- a/public/javascripts/admin_interface.js +++ b/public/javascripts/admin_interface.js | |||
| @@ -67,6 +67,10 @@ $(document).ready(function () { | |||
| 67 | rrule_builder.initialize(); | 67 | rrule_builder.initialize(); |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | if ($("#preview_panel").length != 0) { | ||
| 71 | cccms.preview.initialize(); | ||
| 72 | } | ||
| 73 | |||
| 70 | if ($('#recent_changes_toggle').length != 0) { | 74 | if ($('#recent_changes_toggle').length != 0) { |
| 71 | hide_all(); | 75 | hide_all(); |
| 72 | $('#recent_changes_toggle').attr("class", "selected"); | 76 | $('#recent_changes_toggle').attr("class", "selected"); |
| @@ -165,7 +169,8 @@ cccms = { | |||
| 165 | } | 169 | } |
| 166 | 170 | ||
| 167 | jQuery.fn.submitWithAjax = function(options) { | 171 | jQuery.fn.submitWithAjax = function(options) { |
| 168 | if (page.title_has_changed() || page.abstract_has_changed() || page.body_has_changed()) { | 172 | options = options || {}; |
| 173 | if (options.force || page.title_has_changed() || page.abstract_has_changed() || page.body_has_changed()) { | ||
| 169 | 174 | ||
| 170 | tinymce.triggerSave(); | 175 | tinymce.triggerSave(); |
| 171 | page.cached_title = elements.title.val(); | 176 | page.cached_title = elements.title.val(); |
| @@ -181,6 +186,9 @@ cccms = { | |||
| 181 | type: "POST", | 186 | type: "POST", |
| 182 | url: this.attr("data-autosave-url"), | 187 | url: this.attr("data-autosave-url"), |
| 183 | data: data, | 188 | data: data, |
| 189 | success: function() { | ||
| 190 | cccms.preview.refresh_if_open(); | ||
| 191 | }, | ||
| 184 | error: function(xhr) { | 192 | error: function(xhr) { |
| 185 | if (xhr.status === 423) { | 193 | if (xhr.status === 423) { |
| 186 | clearInterval(cccms.autosave_timer); | 194 | clearInterval(cccms.autosave_timer); |
| @@ -211,6 +219,39 @@ cccms = { | |||
| 211 | $banner.append("."); | 219 | $banner.append("."); |
| 212 | 220 | ||
| 213 | $flash.html($banner); | 221 | $flash.html($banner); |
| 222 | }, | ||
| 223 | |||
| 224 | preview : { | ||
| 225 | is_open : false, | ||
| 226 | |||
| 227 | initialize : function() { | ||
| 228 | var $panel = $('#preview_panel'); | ||
| 229 | var $toggle = $('#preview_toggle'); | ||
| 230 | var $force = $('#preview_force_render'); | ||
| 231 | |||
| 232 | $toggle.on('click', function() { | ||
| 233 | cccms.preview.is_open = !cccms.preview.is_open; | ||
| 234 | $panel.toggle(cccms.preview.is_open); | ||
| 235 | $force.toggle(cccms.preview.is_open); | ||
| 236 | $toggle.attr('aria-pressed', cccms.preview.is_open); | ||
| 237 | if (cccms.preview.is_open) cccms.preview.refresh(); | ||
| 238 | }); | ||
| 239 | |||
| 240 | $force.on('click', function() { | ||
| 241 | $("#page_editor > form").submitWithAjax({ force: true }); | ||
| 242 | }); | ||
| 243 | }, | ||
| 244 | |||
| 245 | refresh : function() { | ||
| 246 | var $iframe = $('#live_preview_iframe'); | ||
| 247 | if ($iframe.length === 0) return; | ||
| 248 | var base = $iframe.data('src'); | ||
| 249 | $iframe.attr('src', base + (base.indexOf('?') === -1 ? '?' : '&') + '_t=' + Date.now()); | ||
| 250 | }, | ||
| 251 | |||
| 252 | refresh_if_open : function() { | ||
| 253 | if (cccms.preview.is_open) cccms.preview.refresh(); | ||
| 254 | } | ||
| 214 | } | 255 | } |
| 215 | } | 256 | } |
| 216 | 257 | ||
diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css index 93cc3f5..5f7723b 100644 --- a/public/stylesheets/admin.css +++ b/public/stylesheets/admin.css | |||
| @@ -683,6 +683,10 @@ table.user_table td.user_login { | |||
| 683 | display: block; | 683 | display: block; |
| 684 | margin-bottom: 1rem; | 684 | margin-bottom: 1rem; |
| 685 | } | 685 | } |
| 686 | |||
| 687 | .body_toolbar_row { | ||
| 688 | margin-left: 120px; | ||
| 689 | } | ||
| 686 | } | 690 | } |
| 687 | @media(max-width:1015px) { | 691 | @media(max-width:1015px) { |
| 688 | div.node_description { | 692 | div.node_description { |
| @@ -1324,3 +1328,67 @@ div#image_browser ul li { | |||
| 1324 | border-radius: 4px; | 1328 | border-radius: 4px; |
| 1325 | } | 1329 | } |
| 1326 | 1330 | ||
| 1331 | /* ============================================================ | ||
| 1332 | Live edit preview | ||
| 1333 | ============================================================ */ | ||
| 1334 | |||
| 1335 | #editor_and_preview { | ||
| 1336 | display: flex; | ||
| 1337 | flex-direction: column; | ||
| 1338 | gap: 1.5rem; | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | @media (min-width: 1016px) { | ||
| 1342 | #editor_and_preview { | ||
| 1343 | flex-direction: row; | ||
| 1344 | align-items: flex-start; | ||
| 1345 | } | ||
| 1346 | |||
| 1347 | #editor_and_preview .preview_content { | ||
| 1348 | flex: 1; | ||
| 1349 | min-width: 0; | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | #preview_panel { | ||
| 1353 | flex: 1; | ||
| 1354 | min-width: 0; | ||
| 1355 | } | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | #preview_panel iframe { | ||
| 1359 | width: 100%; | ||
| 1360 | height: 612px; | ||
| 1361 | border: 0.5px solid var(--border); | ||
| 1362 | border-radius: var(--radius); | ||
| 1363 | } | ||
| 1364 | |||
| 1365 | .body_toolbar_row { | ||
| 1366 | min-height: 2rem; | ||
| 1367 | margin-bottom: 1rem; | ||
| 1368 | display: flex; | ||
| 1369 | gap: 8px; | ||
| 1370 | } | ||
| 1371 | |||
| 1372 | .view_toggle { | ||
| 1373 | background: #f7f7f7; | ||
| 1374 | border: 1px solid #ddd; | ||
| 1375 | border-radius: 4px; | ||
| 1376 | padding: 4px 8px; | ||
| 1377 | cursor: pointer; | ||
| 1378 | color: #555; | ||
| 1379 | display: flex; | ||
| 1380 | align-items: center; | ||
| 1381 | gap: 1em; | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | .view_toggle:hover { | ||
| 1385 | background: #ececec; | ||
| 1386 | border-color: #ccc; | ||
| 1387 | color: #222; | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | .view_toggle[aria-pressed="true"] { | ||
| 1391 | background: #dde8f5; | ||
| 1392 | border-color: #a9c6e8; | ||
| 1393 | color: #1a4d8f; | ||
| 1394 | } | ||
diff --git a/public/stylesheets/ccc.css b/public/stylesheets/ccc.css index fe2144f..bb4f7e3 100644 --- a/public/stylesheets/ccc.css +++ b/public/stylesheets/ccc.css | |||
| @@ -610,3 +610,20 @@ label[for=light-mode] { | |||
| 610 | } | 610 | } |
| 611 | } | 611 | } |
| 612 | 612 | ||
| 613 | .chapter_partial_layout { | ||
| 614 | display: flex; | ||
| 615 | gap: 12px; | ||
| 616 | align-items: flex-start; | ||
| 617 | } | ||
| 618 | |||
| 619 | .chapter_thumbnail img { | ||
| 620 | width: 64px; | ||
| 621 | height: 64px; | ||
| 622 | object-fit: cover; | ||
| 623 | border-radius: 4px; | ||
| 624 | display: block; | ||
| 625 | } | ||
| 626 | |||
| 627 | .chapter_partial_content { | ||
| 628 | min-width: 0; | ||
| 629 | } | ||
