Skip to content

Commit 7321850

Browse files
Editor Improvements (#278)
* Editor Improvements - renders sponsors banner and footer server-side by replacing embedded custom tags - changes preview process in editor by refreshing iframe with the unpublished body so that tags get embedded - adds ability to drop images into CodeMirror - makes CodeMirror the initial default editor instead of tinyMCE - adds beforeunload warning when there are unsaved editor changes - adds max-width on resize editor form and preview windows - scopes sponsors in banner and footer to current website * use post method on editor update Co-authored-by: Cody Brooks <[email protected]>
1 parent 9f536c4 commit 7321850

File tree

14 files changed

+135
-65
lines changed

14 files changed

+135
-65
lines changed

app/assets/stylesheets/modules/_staff-website-page.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
resize: both;
1919
border: 1px solid black;
2020
height: 70vh;
21+
max-width: 70vw;
2122
.inner {
2223
flex-grow: 1;
2324
margin: 0;

app/controllers/sponsors_controller.rb

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,4 @@ def show
55
@sponsors_by_tier = Sponsor.published.order_by_tier.group_by(&:tier)
66
render layout: "themes/#{current_website.theme}"
77
end
8-
9-
def sponsors_footer
10-
@sponsors_in_footer = Sponsor.published.with_footer_image.order_by_tier
11-
render layout: false
12-
end
13-
14-
def banner_ads
15-
@sponsors_in_banner = Sponsor.published.with_banner_ad
16-
render layout: false
17-
end
188
end

app/controllers/staff/pages_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def index
99
end
1010

1111
def show
12-
@body = @page.unpublished_body || ""
12+
@body = params[:preview] || @page.unpublished_body || ""
1313
render template: 'pages/show', layout: "themes/#{current_website.theme}"
1414
end
1515

app/decorators/website_decorator.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,12 @@ def contact_email
2424
def closes_at
2525
event.closes_at(:month_day_year)
2626
end
27+
28+
def banner_sponsors
29+
event.sponsors.published.with_banner_ad
30+
end
31+
32+
def sponsors_in_footer
33+
event.sponsors.published.with_footer_image.order_by_tier
34+
end
2735
end

app/helpers/page_helper.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module PageHelper
2+
TAGS = {
3+
"<sponsors-banner-adds></sponsors-banner-adds>" => "sponsors/banner_ads",
4+
"<sponsors-footer></sponsors-footer>" => "sponsors/sponsors_footer"
5+
}
6+
7+
def embed(body)
8+
body.tap do |body|
9+
TAGS.each do |tag, template|
10+
body.gsub!(tag, render(template: template, layout: false))
11+
end
12+
end.html_safe
13+
end
14+
end

app/javascript/controllers/banner_ads_controller.js

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,7 @@ export default class extends Controller {
77
}
88

99
connect() {
10-
const path = `/${this.eventSlugValue}/banner_ads`
11-
fetch(path)
12-
.then((res) => res.text())
13-
.then((html) => {
14-
const fragment = document
15-
.createRange()
16-
.createContextualFragment(html);
17-
this.element.appendChild(fragment);
18-
19-
this.config()
20-
})
10+
this.config()
2111
}
2212

2313
config() {

app/javascript/controllers/editor_controller.js

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import 'codemirror/mode/htmlmixed/htmlmixed.js'
44

55
export default class extends Controller {
66
static targets = ['htmlContent', 'wysiwygContent', 'wysiwyg', 'html']
7+
static values = { changed: { type: Boolean, default: false } }
78

89
initialize () {
9-
this.defaults = {
10+
this.tinyMCEDefaults = {
1011
height: 500,
1112
menubar: false,
1213
plugins: [
@@ -24,16 +25,14 @@ export default class extends Controller {
2425
images_file_types: 'jpeg,jpg,jpe,jfi,jif,jfif,png,gif,bmp,webp,svg',
2526
relative_urls: false,
2627
convert_urls: false,
27-
init_instance_callback: function(editor) {
28-
var preview = document.getElementById('page-preview');
29-
preview.contentWindow.document.getElementById("content").innerHTML = editor.getContent();
30-
editor.on('input', function(e) {
31-
var preview = document.getElementById('page-preview');
32-
preview.contentWindow.document.getElementById("content").innerHTML = e.target.innerHTML;
28+
init_instance_callback: (editor) => {
29+
editor.on('input', (e) => {
30+
this.preview(e.target.innerHTML);
31+
this.changedValue = true;
3332
});
34-
editor.on('change', function(e) {
35-
var preview = document.getElementById('page-preview');
36-
preview.contentWindow.document.getElementById("content").innerHTML = e.target.getContent();
33+
editor.on('change', (e) => {
34+
this.preview(e.target.getContent());
35+
this.changedValue = true;
3736
});
3837
}
3938
}
@@ -44,16 +43,36 @@ export default class extends Controller {
4443
this.wysiwygTarget.classList.add("hidden");
4544
this.htmlTarget.classList.remove("hidden");
4645
this.htmlContentTarget.disabled = false;
46+
this.initializeCodeMirror().setValue(this.wysiwygEditor.getContent());
47+
}
48+
49+
initializeCodeMirror() {
4750
var editor = CodeMirror.fromTextArea(this.htmlContentTarget, {
4851
mode: "htmlmixed",
4952
lineWrapping: true,
5053
});
51-
editor.setValue(this.wysiwygEditor.getContent());
5254
for (var i=0;i<editor.lineCount();i++) { editor.indentLine(i); }
53-
editor.on('change', function(e) {
54-
var preview = document.getElementById('page-preview');
55-
preview.contentWindow.document.getElementById("content").innerHTML = e.getValue();
55+
editor.on('change', (e) => {
56+
this.changedValue = true;
57+
this.preview(e.getValue());
5658
})
59+
editor.on('drop', (e, event) => {
60+
event.preventDefault();
61+
this.uploadFile(event.dataTransfer.files[0], event, e)
62+
})
63+
return editor;
64+
}
65+
66+
preview(content) {
67+
this.debounce(function() {
68+
document.getElementById('hidden-preview').value = content;
69+
document.getElementById('preview-form').submit();
70+
}, 1000)
71+
}
72+
73+
debounce(func, delay) {
74+
if(this.timeout) { clearTimeout(this.timeout) }
75+
this.timeout = setTimeout(func, delay);
5776
}
5877

5978
wysiwyg(e) {
@@ -65,6 +84,28 @@ export default class extends Controller {
6584
this.htmlEditor.toTextArea();
6685
}
6786

87+
uploadFile(file, event, editor) {
88+
let url = '/image_uploads'
89+
let formData = new FormData()
90+
91+
formData.append('file', file)
92+
93+
fetch(url, {
94+
method: 'POST',
95+
body: formData
96+
}).then(response => response.json())
97+
.then(data => {
98+
let newline = `<img src="${data.location}"/>`
99+
let doc= editor.getDoc()
100+
editor.focus()
101+
let x = event.pageX
102+
let y = event.pageY
103+
editor.setCursor(editor.coordsChar({left:x,top:y}))
104+
let newpos = editor.getCursor()
105+
doc.replaceRange(newline, newpos)
106+
})
107+
}
108+
68109
get wysiwygEditor() {
69110
return tinyMCE.activeEditor;
70111
}
@@ -73,9 +114,22 @@ export default class extends Controller {
73114
return document.querySelector('.CodeMirror').CodeMirror;
74115
}
75116

117+
leavingPage(event) {
118+
console.log(this.changedValue);
119+
if (this.changedValue) {
120+
event.returnValue = "Are you sure you want to leave with unsaved changes?";
121+
return event.returnValue;
122+
}
123+
}
124+
125+
allowFormSubmission(event) {
126+
this.changedValue = false;
127+
}
128+
76129
connect () {
77-
let config = Object.assign({ target: this.wysiwygContentTarget }, this.defaults)
130+
let config = Object.assign({ target: this.wysiwygContentTarget }, this.tinyMCEDefaults)
78131
tinyMCE.init(config)
132+
this.initializeCodeMirror();
79133
}
80134

81135
disconnect () {

app/views/pages/show.html.haml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
= @body.html_safe
1+
= embed(@body)
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1-
.banner-ad-wrapper
2-
- @sponsors_in_banner.each do |sponsor|
3-
= link_to image_tag( sponsor.banner_ad, alt: sponsor.name), sponsor.url, target: "_blank", class: "banner-ad-item", data: { 'banner-ads-target': 'ad' }
1+
%div{ "data-controller" => "banner-ads" }
2+
.banner-ad-wrapper
3+
- current_website.banner_sponsors.each do |sponsor|
4+
= link_to image_tag(sponsor.banner_ad, alt: sponsor.name),
5+
sponsor.url,
6+
target: "_blank",
7+
class: "banner-ad-item",
8+
data: { 'banner-ads-target': 'ad' }
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
%h3.section-title
22
Sponsors
33
.sponsors-footer-wrapper
4-
= render partial: "sponsor_footer", collection: @sponsors_in_footer, as: :sponsor
4+
= render partial: "sponsors/sponsor_footer",
5+
collection: current_website.sponsors_in_footer,
6+
as: :sponsor

0 commit comments

Comments
 (0)