panda_cms 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/builds/panda_cms.css +17 -0
  3. data/app/assets/config/panda_cms_manifest.js +1 -0
  4. data/app/components/panda_cms/code_component.rb +3 -1
  5. data/app/components/panda_cms/menu_component.html.erb +3 -0
  6. data/app/components/panda_cms/menu_component.rb +8 -1
  7. data/app/components/panda_cms/page_menu_component.html.erb +7 -5
  8. data/app/components/panda_cms/page_menu_component.rb +3 -1
  9. data/app/components/panda_cms/rich_text_component.html.erb +2 -2
  10. data/app/components/panda_cms/rich_text_component.rb +12 -4
  11. data/app/components/panda_cms/text_component.rb +1 -1
  12. data/app/controllers/panda_cms/admin/block_contents_controller.rb +1 -1
  13. data/app/javascript/panda_cms/@editorjs--editorjs.js +2577 -0
  14. data/app/javascript/panda_cms/controllers/editor_controller.js +247 -0
  15. data/app/javascript/panda_cms/controllers/index.js +10 -7
  16. data/app/javascript/panda_cms/editor/plain_text_editor.js +102 -0
  17. data/app/javascript/panda_cms/editor/resource_loader.js +69 -0
  18. data/app/javascript/panda_cms/editor/rich_text_editor.js +89 -0
  19. data/app/lib/panda_cms/demo_site_generator.rb +0 -2
  20. data/app/lib/panda_cms/editor_js/blocks/alert.rb +32 -0
  21. data/app/lib/panda_cms/editor_js/blocks/base.rb +28 -0
  22. data/app/lib/panda_cms/editor_js/blocks/header.rb +13 -0
  23. data/app/lib/panda_cms/editor_js/blocks/image.rb +34 -0
  24. data/app/lib/panda_cms/editor_js/blocks/list.rb +30 -0
  25. data/app/lib/panda_cms/editor_js/blocks/paragraph.rb +13 -0
  26. data/app/lib/panda_cms/editor_js/blocks/quote.rb +27 -0
  27. data/app/lib/panda_cms/editor_js/blocks/table.rb +48 -0
  28. data/app/lib/panda_cms/editor_js/renderer.rb +120 -0
  29. data/app/models/panda_cms/block_content.rb +12 -2
  30. data/app/views/panda_cms/admin/pages/edit.html.erb +10 -9
  31. data/app/views/panda_cms/shared/_header.html.erb +2 -3
  32. data/app/views/panda_cms/shared/_importmap.html.erb +13 -3
  33. data/config/importmap.rb +3 -1
  34. data/db/migrate/20240315125411_add_status_to_panda_cms_pages.rb +5 -3
  35. data/db/migrate/20241031205109_add_cached_content_to_panda_cms_block_contents.rb +5 -0
  36. data/db/seeds.rb +1 -0
  37. data/lib/panda_cms/engine.rb +16 -8
  38. data/lib/panda_cms/version.rb +1 -1
  39. metadata +89 -103
  40. data/app/javascript/panda_cms/panda_cms_editable.js +0 -248
@@ -1,248 +0,0 @@
1
- /**
2
- * Represents a controller for managing editable content within an iframe.
3
- * @class
4
- */
5
- class PandaCmsEditableController {
6
- /**
7
- * Represents the constructor for the Editable class.
8
- * @param {HTMLIFrameElement} frame - The iFrame element to be used for editing.
9
- */
10
- constructor(pageId, frame) {
11
- this.pageId = pageId;
12
- this.frame = frame;
13
- this.frame.style.display = "none";
14
- this.csrfToken = document.querySelector('meta[name="csrf-token"]').content;
15
-
16
- var pathNameArray = window.location.pathname.split("/");
17
- this.adminPath = pathNameArray[1];
18
-
19
- this.frame.addEventListener("load", () => {
20
- this.frameDocument = this.frame.contentDocument || this.frame.contentWindow.document;
21
- this.body = this.frameDocument.body;
22
- this.head = this.frameDocument.head;
23
- this.loadEvents();
24
- });
25
- }
26
-
27
- /* iFrame & Main Methods */
28
-
29
- /**
30
- * Load events for the editable iFrame
31
- */
32
- loadEvents() {
33
- console.debug("[Panda CMS] iFrame loaded...");
34
-
35
- this.embedPlainTextEditors();
36
- this.embedTrix();
37
- }
38
-
39
- setFrameVisible() {
40
- console.debug("[Panda CMS] Setting iFrame to visible...");
41
- this.frame.style.display = "";
42
- }
43
-
44
- addStylesheet(frameDocument, head, href) {
45
- return new Promise(function (resolve, reject) {
46
- let link = frameDocument.createElement("link");
47
- link.rel = "stylesheet";
48
- link.href = href;
49
- link.media = "none";
50
- head.append(link);
51
-
52
- link.onload = () => {
53
- if (link.media != "all") {
54
- link.media = "all";
55
- }
56
- console.debug(`[Panda CMS] Stylesheet loaded: ${href}`);
57
- resolve(link);
58
- };
59
- link.onerror = () =>
60
- reject(new Error(`[Panda CMS] Stylesheet load error for ${href}`));
61
- });
62
- }
63
-
64
- loadScript(frameDocument, head, src) {
65
- return new Promise(function (resolve, reject) {
66
- let script = frameDocument.createElement("script");
67
- script.src = src;
68
- head.append(script);
69
-
70
- script.onload = () => {
71
- console.debug(`[Panda CMS] Script loaded: ${src}`);
72
- resolve(script);
73
- };
74
- script.onerror = () =>
75
- reject(new Error(`[Panda CMS] Script load error for ${src}`));
76
- });
77
- }
78
-
79
- /* Plain Text Editor (inc. Code) Methods */
80
-
81
- stylePlainTextEditor(element, status) {
82
- console.debug(
83
- `[Panda CMS] Styling editor ${element.id} as ${status}...`
84
- );
85
-
86
- if (status == "initial") {
87
- element.style.border = "1px dashed #ccc";
88
- element.style.outline = "none";
89
- element.style.cursor = "pointer";
90
- element.style.transition = "background 500ms linear";
91
- element.style.backgroundColor = "inherit";
92
- } else if (status == "success") {
93
- element.style.backgroundColor = "#66bd6a50";
94
- } else if (status == "error") {
95
- element.style.backgroundColor = "#dc354550";
96
- }
97
-
98
- if (element.getAttribute("data-editable-kind") == "html") {
99
- element.style.whiteSpace = "pre-wrap";
100
- element.style.fontFamily = "monospace";
101
- }
102
- }
103
-
104
- embedPlainTextEditors() {
105
- var elements = this.body.querySelectorAll('[data-editable-kind="plain_text"], [data-editable-kind="markdown"], [data-editable-kind="html"]');
106
- if (elements.length == 0) {
107
- return;
108
- }
109
-
110
- elements.forEach((element) => {
111
- this.stylePlainTextEditor(element, "initial");
112
- var currentElement = element;
113
-
114
- // On save click, save this element
115
- document.getElementById('saveEditableButton').addEventListener('click', () => {
116
- this.bindPlainTextSaveHandler(currentElement);
117
- });
118
-
119
- console.debug("[Panda CMS] Attached button event handler to ${currentElement.id}");
120
- });
121
-
122
- console.debug(
123
- "[Panda CMS] Dispatching event: pandaCmsPlainTextEditorLoaded"
124
- );
125
-
126
- // Let the parent know that the external resources have been loaded
127
- this.frameDocument.dispatchEvent(new Event("pandaCmsPlainTextEditorLoaded"));
128
- }
129
-
130
- bindPlainTextSaveHandler(target) {
131
- var blockContentId = target.getAttribute(
132
- "data-editable-block-content-id"
133
- );
134
-
135
- if (target.getAttribute("data-editable-kind") == "html") { // Or markdown?
136
- var content = target.innerText;
137
- } else {
138
- var content = target.innerHTML;
139
- }
140
-
141
- fetch(`/${this.adminPath}/pages/${this.pageId}/block_contents/${blockContentId}`, {
142
- method: "PATCH",
143
- headers: {
144
- "Content-Type": "application/json",
145
- "X-CSRF-Token": document
146
- .querySelector('meta[name="csrf-token"]')
147
- .getAttribute("content"),
148
- },
149
- body: JSON.stringify({ content: content }),
150
- })
151
- .then((response) => response.json())
152
- .then((data) => {
153
- this.stylePlainTextEditor(target, "success");
154
- setTimeout(() => {
155
- this.stylePlainTextEditor(target, "initial");
156
- }, 1000);
157
- })
158
- .catch((error) => {
159
- this.stylePlainTextEditor(target, "error");
160
- setTimeout(() => {
161
- this.stylePlainTextEditor(target, "initial");
162
- }, 1000);
163
- alert("Error:", error);
164
- console.log(error);
165
- });
166
- }
167
-
168
- /* Rich Text Editor Methods */
169
-
170
- embedTrix() {
171
- // Our stylesheet includes dist trix.css as of v2.1.7
172
- this.addStylesheet(this.frameDocument, this.head, "/panda-cms-assets/rich_text_editor.css");
173
- this.loadScript(this.frameDocument, this.head, "https://cdn.jsdelivr.net/npm/trix/dist/trix.js");
174
-
175
- this.body.addEventListener("trix-before-initialize", () => {
176
- // Change Trix.config if you need
177
- console.debug("[Panda CMS] Trix before initialize");
178
- // Trix.config.blockAttributes.heading = {
179
- // tagName: "h2",
180
- // terminal: true,
181
- // breakOnReturn: true,
182
- // group: false
183
- // }
184
-
185
- // Trix.config.blockAttributes.subHeading = {
186
- // tagName: "h3",
187
- // terminal: true,
188
- // breakOnReturn: true,
189
- // group: false
190
- // }
191
-
192
- // To make Trix styles appear we should define them in nb surely?
193
- // Defining e.g. .panda-cms-content h2 in panda won't show it in panda, but
194
- // as this is a page in nb
195
- // We can define these styles through admin or a custom stylesheet eventually?
196
-
197
- console.log(Trix.config.blockAttributes);
198
- })
199
-
200
- this.body.addEventListener("trix-initialize", (event) => {
201
- const { toolbarElement } = event.target
202
- const h1Button = toolbarElement.querySelector("[data-trix-attribute=heading1]")
203
- h1Button.insertAdjacentHTML("afterend", `
204
- <button type="button" class="trix-button" data-trix-attribute="heading2" title="Heading 2" tabindex="-1" data-trix-active="">H2</button>
205
- `)
206
- })
207
-
208
- document.getElementById('saveEditableButton').addEventListener('click', () => {
209
- console.debug("[Panda CMS] Handling click event on #saveEditableButton");
210
- // Grab each input element that's a rich text editor and append a save handler to the button
211
- var elements = this.frameDocument.querySelectorAll("input[data-editor-type='rich-text']");
212
- elements.forEach((element) => {
213
- var blockContentId = element.getAttribute("data-editor-block-content-id");
214
- this.bindTrixSaveHandler(blockContentId, element.value);
215
- });
216
- });
217
-
218
- // This prevents the flash of content before the iFrame is ready
219
- console.debug("[Panda CMS] Setting iFrame to visible");
220
- this.setFrameVisible();
221
- }
222
-
223
- bindTrixSaveHandler(blockContentId, content) {
224
- console.debug(`[Panda CMS] Calling save handler for ${blockContentId}...`);
225
- fetch(`/${this.adminPath}/pages/${this.pageId}/block_contents/${blockContentId}`, {
226
- method: "PATCH",
227
- headers: {
228
- "Content-Type": "application/json",
229
- "X-CSRF-Token": this.csrfToken,
230
- },
231
- body: JSON.stringify({ content: content }),
232
- })
233
- .then((response) => response.json())
234
- .then((data) => {
235
- console.debug("[Panda CMS] Save successful for block content ID:", blockContentId);
236
- document.getElementById('saveEditableButton').classList.remove("bg-inactive");
237
- document.getElementById('saveEditableButton').classList.add("bg-active");
238
- setTimeout(() => {
239
- document.getElementById('saveEditableButton').classList.remove("bg-active");
240
- document.getElementById('saveEditableButton').classList.add("bg-inactive");
241
- }, 1500);
242
- })
243
- .catch((error) => {
244
- console.log(error);
245
- alert("Error updating. Please contact the support team!", error);
246
- });
247
- }
248
- }