Skip to content

Commit a01ba83

Browse files
Merge pull request #9 from prathamVaidya/issue-8-autosave-without-switching-modes
🎨 Added autosave without switching modes
2 parents 8fbd6bd + 0ac0f56 commit a01ba83

File tree

5 files changed

+129
-57
lines changed

5 files changed

+129
-57
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "supercode-tinymce-plugin",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"description": "An Enhanced and open source Code editor for TinyMCE with improved integration with TinyMCE view",
55
"main": "index.js",
66
"scripts": {
7-
"build": "cp src/* supercode/ -r && vite build",
7+
"build": "cp -r src/* supercode/ && vite build",
88
"start": "rm -rf public/tinymce && cp -r node_modules/tinymce public && storybook dev -p 6006",
99
"build-storybook": "cp -r node_modules/tinymce public && storybook build",
1010
"test": "echo \"Error: no test specified\" && exit 1"

src/plugin.js

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Repository: https://github.com/prathamVaidya/supercode-tinymce-plugin
66
* Author: Pratham Vaidya
77
* License: GPL-3.0
8-
* Version: 1.2.0
8+
* Version: 1.2.1
99
*
1010
* Released under the GPL-3.0 License.
1111
*/
@@ -192,6 +192,14 @@
192192
document.head.append(style);
193193
}
194194

195+
const debounce = (func, delay) => {
196+
let timeoutId;
197+
return (...args) => {
198+
clearTimeout(timeoutId);
199+
timeoutId = setTimeout(() => func.apply(null, args), delay);
200+
};
201+
};
202+
195203
// on plugin load
196204
const mainPlugin = function(editor) {
197205

@@ -450,20 +458,34 @@
450458
modal.element.style.display = 'none';
451459
}, 10)
452460
};
461+
462+
// Save editor content to tinymce editor on ace editor change
463+
const liveSave = () => {
464+
editor.undoManager.transact(function() {
465+
let value = aceEditor.getValue();
466+
if(Config.renderer){
467+
value = Config.renderer(value);
468+
}
469+
editor.setContent(value);
470+
});
471+
editor.nodeChanged();
472+
};
453473

454474
const onSaveHandler = () => {
455475
editor.focus();
456-
editor.undoManager.transact(function() {
457-
let value = aceEditor.getValue();
458-
if(Config.renderer){
459-
value = Config.renderer(value);
476+
if (Config.fallbackModal) {
477+
editor.undoManager.transact(function() {
478+
let value = aceEditor.getValue();
479+
if(Config.renderer){
480+
value = Config.renderer(value);
481+
}
482+
editor.setContent(value);
483+
});
484+
editor.selection.setCursorLocation();
485+
editor.nodeChanged();
460486
}
461-
editor.setContent(value);
462-
});
463-
editor.selection.setCursorLocation();
464-
editor.nodeChanged();
465487
editor.execCommand('ToggleView', false, 'supercode');
466-
}
488+
};
467489

468490
const onKeyDownHandler = (e) => {
469491
if((e.key === ' ' && e.ctrlKey) || e.key === 'Escape'){
@@ -484,6 +506,11 @@
484506
if(!session){
485507
session = ace.createEditSession(content, `ace/mode/${Config.language}`);
486508
session.setUseWrapMode(Config.wrap);
509+
// Attach live save to ace editor for in-editor source view
510+
if (!Config.fallbackModal) {
511+
const debouncedSave = debounce(liveSave, 300);
512+
session.on('change', debouncedSave);
513+
}
487514
}
488515
aceEditor.setSession(session);
489516
session.setValue(content);

stories/TinymceEditor.jsx

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import React, { useRef } from 'react';
22
import { Editor } from '@tinymce/tinymce-react';
33
import '../public/tinymce/tinymce.js'
44

5-
6-
75
const sizes = {
86
'none': {
97

@@ -22,20 +20,7 @@ const sizes = {
2220
}
2321
}
2422

25-
export default function TinymceEditor({ skin = 'oxide', mode, value, size = 'none'}) {
26-
const editorRef = useRef(null);
27-
28-
const commonConfiguration = {
29-
skin,
30-
base_url: '/tinymce',
31-
promotion: false,
32-
statusbar: false,
33-
menubar: false,
34-
highlight_on_focus: false,
35-
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
36-
}
37-
38-
const modes = {
23+
const modes = {
3924
"super" : {
4025
plugins: [
4126
'advlist', 'autolink', 'lists', 'link', 'anchor',
@@ -45,6 +30,9 @@ export default function TinymceEditor({ skin = 'oxide', mode, value, size = 'non
4530
'bold italic forecolor | alignleft aligncenter ' +
4631
'alignright alignjustify | bullist numlist outdent indent | ' +
4732
'codeeditor | supercode',
33+
supercode: {
34+
fontSize: 12,
35+
}
4836
},
4937
"classic" : {
5038
plugins: [
@@ -58,6 +46,22 @@ export default function TinymceEditor({ skin = 'oxide', mode, value, size = 'non
5846
}
5947
}
6048

49+
export default function TinymceEditor({ skin = 'oxide', mode, value, size = 'none'}) {
50+
const editorRef = useRef(null);
51+
52+
const commonConfiguration = {
53+
skin,
54+
base_url: '/tinymce',
55+
promotion: false,
56+
statusbar: false,
57+
menubar: false,
58+
highlight_on_focus: false,
59+
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
60+
61+
}
62+
63+
64+
6165
React.useEffect(() => {
6266
console.debug('skin reloaded');
6367
}, [skin])

supercode/plugin.js

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Repository: https://github.com/prathamVaidya/supercode-tinymce-plugin
66
* Author: Pratham Vaidya
77
* License: GPL-3.0
8-
* Version: 1.2.0
8+
* Version: 1.2.1
99
*
1010
* Released under the GPL-3.0 License.
1111
*/
@@ -192,6 +192,14 @@
192192
document.head.append(style);
193193
}
194194

195+
const debounce = (func, delay) => {
196+
let timeoutId;
197+
return (...args) => {
198+
clearTimeout(timeoutId);
199+
timeoutId = setTimeout(() => func.apply(null, args), delay);
200+
};
201+
};
202+
195203
// on plugin load
196204
const mainPlugin = function(editor) {
197205

@@ -450,20 +458,34 @@
450458
modal.element.style.display = 'none';
451459
}, 10)
452460
};
461+
462+
// Save editor content to tinymce editor on ace editor change
463+
const liveSave = () => {
464+
editor.undoManager.transact(function() {
465+
let value = aceEditor.getValue();
466+
if(Config.renderer){
467+
value = Config.renderer(value);
468+
}
469+
editor.setContent(value);
470+
});
471+
editor.nodeChanged();
472+
};
453473

454474
const onSaveHandler = () => {
455475
editor.focus();
456-
editor.undoManager.transact(function() {
457-
let value = aceEditor.getValue();
458-
if(Config.renderer){
459-
value = Config.renderer(value);
476+
if (Config.fallbackModal) {
477+
editor.undoManager.transact(function() {
478+
let value = aceEditor.getValue();
479+
if(Config.renderer){
480+
value = Config.renderer(value);
481+
}
482+
editor.setContent(value);
483+
});
484+
editor.selection.setCursorLocation();
485+
editor.nodeChanged();
460486
}
461-
editor.setContent(value);
462-
});
463-
editor.selection.setCursorLocation();
464-
editor.nodeChanged();
465487
editor.execCommand('ToggleView', false, 'supercode');
466-
}
488+
};
467489

468490
const onKeyDownHandler = (e) => {
469491
if((e.key === ' ' && e.ctrlKey) || e.key === 'Escape'){
@@ -484,6 +506,11 @@
484506
if(!session){
485507
session = ace.createEditSession(content, `ace/mode/${Config.language}`);
486508
session.setUseWrapMode(Config.wrap);
509+
// Attach live save to ace editor for in-editor source view
510+
if (!Config.fallbackModal) {
511+
const debouncedSave = debounce(liveSave, 300);
512+
session.on('change', debouncedSave);
513+
}
487514
}
488515
aceEditor.setSession(session);
489516
session.setValue(content);

supercode/plugin.min.js

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
!function() {
22
let e = null;
33
tinymce.PluginManager.add("supercode", function(o) {
4-
let t, n, r, d = 0, a = false;
4+
let t, n, r, a = 0, d = false;
55
const c = { theme: "chrome", fontSize: 14, wrap: true, icon: void 0, iconName: "sourcecode", autocomplete: false, language: "html", renderer: null, parser: null, shortcut: true, aceCss: null, fontFamily: null, fallbackModal: false, modalPrimaryColor: "#ffffff", modalSecondaryColor: "#222f3e", dark: false, debug: true }, s = () => {
66
const e2 = {};
77
c.autocomplete && (e2.enableLiveAutocompletion = true), c.fontFamily && (e2.fontFamily = c.fontFamily), r.setOptions(e2), r.setTheme(`ace/theme/${c.theme}`), r.setFontSize(c.fontSize), r.setShowPrintMargin(false);
8-
}, i = (e2, o2) => {
8+
}, l = (e2, o2) => {
99
const t2 = o2.cloneNode(true);
1010
t2.style.position = "relative";
1111
const n2 = t2.querySelector(".tox-menubar");
1212
n2 && (n2.innerHTML = "<b style='font-size: 14px; font-weight: bold; padding: 11px 9px;'>Source Code Editor</b>");
13-
let r2 = null, d2 = false;
13+
let r2 = null, a2 = false;
1414
t2.querySelectorAll(".tox-tbtn, .tox-split-button").forEach((e3) => {
15-
"supercode" != e3.getAttribute("data-mce-name") ? ("overflow-button" === e3.getAttribute("data-mce-name") && (r2 = e3), e3.classList.remove("tox-tbtn--enabled"), e3.classList.add("tox-tbtn--disabled"), e3.removeAttribute("data-mce-name")) : (d2 = true, e3.setAttribute("data-mce-name", "supercode-toggle"), e3.classList.add("tox-tbtn--enabled"), e3.onclick = y);
16-
}), !d2 && r2 && (r2.classList = "tox-tbtn tox-tbtn--enabled", r2.innerHTML = `<span class="tox-icon tox-tbtn__icon-wrap">${c.icon}</span>`, r2.onclick = y), e2.innerHTML = "", e2.append(t2);
17-
}, l = (e2, o2) => {
15+
"supercode" != e3.getAttribute("data-mce-name") ? ("overflow-button" === e3.getAttribute("data-mce-name") && (r2 = e3), e3.classList.remove("tox-tbtn--enabled"), e3.classList.add("tox-tbtn--disabled"), e3.removeAttribute("data-mce-name")) : (a2 = true, e3.setAttribute("data-mce-name", "supercode-toggle"), e3.classList.add("tox-tbtn--enabled"), e3.onclick = b);
16+
}), !a2 && r2 && (r2.classList = "tox-tbtn tox-tbtn--enabled", r2.innerHTML = `<span class="tox-icon tox-tbtn__icon-wrap">${c.icon}</span>`, r2.onclick = b), e2.innerHTML = "", e2.append(t2);
17+
}, i = (e2, o2) => {
1818
e2.style.width = o2 + "px", e2.style.height = "100%", e2.style.position = "relative", ((e3) => {
1919
if (e3.attachShadow({ mode: "open" }), c.aceCss) {
2020
const o4 = new CSSStyleSheet();
@@ -84,43 +84,57 @@ Use debug:false option to disable this warning`)), c.fallbackModal || (true ===
8484
})("\n\n :root{\n --supercode-modal-primary: #ffffff;\n --supercode-modal-secondary: #222f3e;\n --supercode-modal-border: rgba(0, 0, 0, 0.1);\n }\n\n /* Media query for mobile devices */\n @media only screen and (max-width: 767px) {\n #supercode-modal {\n width: 100% !important;\n height: 100% !important;\n border-radius: 0 !important;\n }\n }\n .disable-scroll {\n overflow: hidden;\n }\n #supercode-modal-container {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 990;\n display: none;\n opacity: 0;\n transition: opacity 0.1s linear;\n }\n #supercode-backdrop {\n position: absolute;\n top: 0;\n left: 0;\n background: black;\n opacity: 0.7;\n width: 100%;\n height: 100%;\n z-index: 1;\n }\n #supercode-modal {\n width: 90%;\n height: 80%;\n max-width: 1200px;\n z-index: 2;\n overflow: hidden;\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n background: var(--supercode-modal-primary);\n }\n #supercode-header {\n display: flex;\n padding: 0.5rem 1rem;\n border-bottom: 1px solid var(--supercode-modal-border);\n color: var(--supercode-modal-secondary);\n }\n #supercode-modal h1 {\n flex-grow: 1;\n margin: auto;\n font-size: 14px;\n }\n #supercode-close-btn {\n background: none;\n border: none;\n padding: 0;\n height: 100%;\n cursor: pointer;\n fill: var(--supercode-modal-secondary);\n }\n #supercode-editor {\n width: 100%;\n height: 100%;\n position: relative;\n }\n #supercode-footer {\n padding: 0.5rem 1rem;\n display: flex;\n justify-content: end;\n gap: 1rem;\n border-top: 1px solid var(--supercode-modal-border);\n }\n #supercode-footer button {\n padding: 0.5rem 1rem;\n border-radius: 5px;\n font-weight: bold;\n border: none;\n cursor: pointer;\n min-width: 5rem;\n transition: opacity 0.1s linear;\n }\n #supercode-footer button:hover {\n opacity: 0.8;\n }\n #supercode-cancel-btn {\n background: transparent;\n color: var(--supercode-modal-secondary);\n }\n #supercode-save-btn {\n background: var(--supercode-modal-secondary);\n color: var(--supercode-modal-primary);\n }\n "), document.body.append(o2), e = { element: o2, editor: ace.edit(o2.querySelector("#supercode-editor")) };
8585
}
8686
r = e.editor, s(), e.element.querySelector("#supercode-backdrop").onclick = m, e.element.querySelector("#supercode-close-btn").onclick = m, e.element.querySelector("#supercode-cancel-btn").onclick = m, e.element.querySelector("#supercode-save-btn").onclick = () => {
87-
y(), m();
87+
b(), m();
8888
}, c.shortcut && e.element.querySelector("#supercode-editor").addEventListener("keydown", u), document.querySelector("body").classList.add("disable-scroll"), document.body.style.setProperty("--supercode-modal-primary", c.modalPrimaryColor), document.body.style.setProperty("--supercode-modal-secondary", c.modalSecondaryColor), c.dark && document.body.style.setProperty("--supercode-modal-border", "rgba(255, 255, 255, 0.1)"), e.element.querySelector("#supercode-close-btn").innerHTML = ((_c = (_b = (_a = o.ui.registry).getAll) == null ? void 0 : _b.call(_a).icons) == null ? void 0 : _c.close) ?? '<svg width="24" height="24"><path d="M17.3 8.2 13.4 12l3.9 3.8a1 1 0 0 1-1.5 1.5L12 13.4l-3.8 3.9a1 1 0 0 1-1.5-1.5l3.9-3.8-3.9-3.8a1 1 0 0 1 1.5-1.5l3.8 3.9 3.8-3.9a1 1 0 0 1 1.5 1.5Z" fill-rule="evenodd"></path></svg>', e.element.style.display = "flex", setTimeout(() => {
8989
e.element.style.opacity = 1;
90-
}, 10), h();
90+
}, 10), f();
9191
}, m = () => {
9292
c.shortcut && removeEventListener("keydown", u), document.querySelector("body").classList.remove("disable-scroll"), e.element.style.opacity = 0, o.focus(), setTimeout(() => {
9393
e.element.style.display = "none";
9494
}, 10);
9595
}, y = () => {
96-
o.focus(), o.undoManager.transact(function() {
96+
o.undoManager.transact(function() {
9797
let e2 = r.getValue();
9898
c.renderer && (e2 = c.renderer(e2)), o.setContent(e2);
99-
}), o.selection.setCursorLocation(), o.nodeChanged(), o.execCommand("ToggleView", false, "supercode");
100-
}, b = (e2) => {
101-
(" " === e2.key && e2.ctrlKey || "Escape" === e2.key) && y();
102-
}, h = () => {
99+
}), o.nodeChanged();
100+
}, b = () => {
101+
o.focus(), c.fallbackModal && (o.undoManager.transact(function() {
102+
let e2 = r.getValue();
103+
c.renderer && (e2 = c.renderer(e2)), o.setContent(e2);
104+
}), o.selection.setCursorLocation(), o.nodeChanged()), o.execCommand("ToggleView", false, "supercode");
105+
}, h = (e2) => {
106+
(" " === e2.key && e2.ctrlKey || "Escape" === e2.key) && b();
107+
}, f = () => {
103108
let e2 = (t2 = o.getContent(), c.parser ? c.parser(t2) : html_beautify(t2));
104109
var t2;
105-
n || (n = ace.createEditSession(e2, `ace/mode/${c.language}`), n.setUseWrapMode(c.wrap)), r.setSession(n), n.setValue(e2), r.gotoLine(1 / 0), r.focus();
106-
}, f = function() {
110+
if (!n && (n = ace.createEditSession(e2, `ace/mode/${c.language}`), n.setUseWrapMode(c.wrap), !c.fallbackModal)) {
111+
const e3 = /* @__PURE__ */ ((e4, o2) => {
112+
let t3;
113+
return (...n2) => {
114+
clearTimeout(t3), t3 = setTimeout(() => e4.apply(null, n2), o2);
115+
};
116+
})(y, 300);
117+
n.on("change", e3);
118+
}
119+
r.setSession(n), n.setValue(e2), r.gotoLine(1 / 0), r.focus();
120+
}, g = function() {
107121
if (c.fallbackModal)
108122
p();
109123
else {
110124
const e2 = o.getContainer();
111-
d && (a = d != e2.clientWidth), d = e2.clientWidth, !a && t || (t = e2.querySelector(".tox-editor-header")), o.execCommand("ToggleView", false, "supercode");
125+
a && (d = a != e2.clientWidth), a = e2.clientWidth, !d && t || (t = e2.querySelector(".tox-editor-header")), o.execCommand("ToggleView", false, "supercode");
112126
}
113127
};
114128
if (!c.fallbackModal) {
115129
const e2 = { onShow: (e3) => {
116130
const o2 = e3.getContainer();
117-
a && (i(o2.querySelector(".supercode-header"), t), o2.querySelector(".supercode-body ").style.width = d + "px", r.resize()), 0 === o2.childElementCount && (o2.style.padding = 0, o2.style.display = "flex", o2.style.flexDirection = "column", o2.innerHTML = '<div class="supercode-header"></div><div class="supercode-body"></div>', c.shortcut && o2.addEventListener("keydown", b), i(o2.querySelector(".supercode-header"), t), l(o2.querySelector(".supercode-body "), d)), h();
131+
d && (l(o2.querySelector(".supercode-header"), t), o2.querySelector(".supercode-body ").style.width = a + "px", r.resize()), 0 === o2.childElementCount && (o2.style.padding = 0, o2.style.display = "flex", o2.style.flexDirection = "column", o2.innerHTML = '<div class="supercode-header"></div><div class="supercode-body"></div>', c.shortcut && o2.addEventListener("keydown", h), l(o2.querySelector(".supercode-header"), t), i(o2.querySelector(".supercode-body "), a)), f();
118132
}, onHide: () => {
119-
c.shortcut && removeEventListener("keydown", b);
133+
c.shortcut && removeEventListener("keydown", h);
120134
} };
121135
o.ui.registry.addView("supercode", e2);
122136
}
123-
return o.ui.registry.addButton("supercode", { icon: c.iconName, tooltip: "Source Code Editor (Ctrl + space)", onAction: f }), o.ui.registry.addMenuItem("supercode", { icon: c.iconName, text: "Source Code", onAction: f }), o.ui.registry.addContextMenu("supercode", { update: (e2) => "supercode" }), c.shortcut && o.shortcuts.add("ctrl+32", "Toggles Source Code Editing Mode", f), { getMetadata: function() {
137+
return o.ui.registry.addButton("supercode", { icon: c.iconName, tooltip: "Source Code Editor (Ctrl + space)", onAction: g }), o.ui.registry.addMenuItem("supercode", { icon: c.iconName, text: "Source Code", onAction: g }), o.ui.registry.addContextMenu("supercode", { update: (e2) => "supercode" }), c.shortcut && o.shortcuts.add("ctrl+32", "Toggles Source Code Editing Mode", g), { getMetadata: function() {
124138
return { name: "Supercode", url: "https://github.com/prathamVaidya/supercode-tinymce-plugin" };
125139
} };
126140
});

0 commit comments

Comments
 (0)