summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/views/nodes/edit.html.erb21
-rw-r--r--app/views/page_translations/edit.html.erb24
-rw-r--r--public/javascripts/admin_interface.js43
-rw-r--r--public/stylesheets/admin.css68
4 files changed, 149 insertions, 7 deletions
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}