enrollment-app/web/email-editor.html
2026-07-02 11:06:41 +05:30

195 lines
5.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Email Editor</title>
<link rel="stylesheet" href="https://unpkg.com/grapesjs/dist/css/grapes.min.css" />
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
background: #f5f5f5;
font-family: Arial, Helvetica, sans-serif;
}
#gjs { height: 100%; border: none; }
.gjs-one-bg { background-color: #f8f9fa; }
.gjs-two-color { color: #333; }
.gjs-three-bg { background-color: #009195; color: #fff; }
.gjs-four-color,
.gjs-four-color-h:hover { color: #009195; }
.gjs-pn-btn.gjs-pn-active,
.gjs-pn-btn:hover { color: #009195; }
.gjs-block { min-height: 54px; }
</style>
</head>
<body>
<div id="gjs"></div>
<script src="https://unpkg.com/grapesjs"></script>
<script src="https://unpkg.com/grapesjs-preset-newsletter"></script>
<script>
let editor = null;
let pendingHtml = null;
let changeTimer = null;
let lastExportedHtml = '';
let destroyed = false;
function postToParent(payload) {
if (destroyed) return;
if (window.parent && window.parent !== window) {
window.parent.postMessage(JSON.stringify(payload), '*');
}
}
function extractBodyContent(html) {
if (!html) return '';
const match = html.match(/<body[^>]*>([\s\S]*)<\/body>/i);
return match ? match[1].trim() : html;
}
function wrapAsEmailDocument(innerHtml) {
return '<!DOCTYPE html>\n<html>\n<head>\n<meta charset="utf-8">\n<meta name="viewport" content="width=device-width, initial-scale=1">\n</head>\n<body style="margin:0;padding:0;">' + innerHtml + '\n</body>\n</html>';
}
function exportFullHtml() {
if (!editor) return '';
try {
const inlined = editor.runCommand('gjs-get-inlined-html');
return wrapAsEmailDocument(inlined || '');
} catch (e) {
const html = editor.getHtml() || '';
const css = editor.getCss() || '';
return wrapAsEmailDocument('<style>' + css + '</style>' + html);
}
}
function scheduleChange() {
if (destroyed) return;
clearTimeout(changeTimer);
changeTimer = setTimeout(function () {
if (destroyed || !editor) return;
const html = exportFullHtml();
if (!html || html === lastExportedHtml) return;
lastExportedHtml = html;
postToParent({ source: 'nhance-email-editor', action: 'change', html: html });
}, 800);
}
function loadHtml(fullHtml) {
if (destroyed || !editor) {
pendingHtml = fullHtml;
return;
}
const content = extractBodyContent(fullHtml);
editor.setComponents(content);
editor.refresh();
lastExportedHtml = exportFullHtml();
}
function insertToken(token) {
if (destroyed || !editor) return;
const selected = editor.getSelected();
const cmp = selected && selected.get('type') !== 'wrapper'
? selected
: editor.getWrapper().components().at(0);
if (cmp && cmp.append) {
cmp.append('<span>' + token + '</span>');
} else {
editor.addComponents('<span>' + token + '</span>');
}
scheduleChange();
}
function destroyEditor() {
destroyed = true;
clearTimeout(changeTimer);
changeTimer = null;
try {
if (editor) editor.destroy();
} catch (e) {}
editor = null;
}
function initEditor() {
try {
editor = grapesjs.init({
container: '#gjs',
height: '100%',
width: 'auto',
fromElement: false,
storageManager: false,
noticeOnUnload: false,
canvas: {
styles: ['https://fonts.googleapis.com/css?family=Roboto:400,500,700'],
},
deviceManager: {
devices: [
{ name: 'Desktop', width: '' },
{ name: 'Mobile', width: '320px', widthMedia: '480px' },
],
},
plugins: ['grapesjs-preset-newsletter'],
pluginsOpts: {
'grapesjs-preset-newsletter': {
modalLabelImport: 'Paste your HTML',
modalLabelExport: 'Copy HTML',
codeViewerTheme: 'material',
},
},
});
editor.on('update', scheduleChange);
if (pendingHtml) {
loadHtml(pendingHtml);
pendingHtml = null;
}
postToParent({ source: 'nhance-email-editor', action: 'ready' });
} catch (e) {
postToParent({ source: 'nhance-email-editor', action: 'error', message: String(e) });
}
}
window.addEventListener('message', function (event) {
let data = event.data || {};
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (_) {
return;
}
}
if (data.source !== 'nhance-email-editor-host') return;
switch (data.action) {
case 'load':
loadHtml(data.html || '');
break;
case 'export':
postToParent({
source: 'nhance-email-editor',
action: 'html',
requestId: data.requestId,
html: exportFullHtml(),
});
break;
case 'insertToken':
insertToken(data.token || '');
break;
case 'destroy':
destroyEditor();
break;
default:
break;
}
});
initEditor();
</script>
</body>
</html>