mailtrain/client/src/lib/sandboxed-codeeditor-root.js

166 lines
4.9 KiB
JavaScript

'use strict';
import './public-path';
import React, {Component} from 'react';
import ReactDOM
from 'react-dom';
import {
I18nextProvider,
translate,
} from 'react-i18next';
import i18n
from './i18n';
import {
parentRPC,
UntrustedContentRoot
} from './untrusted';
import PropTypes
from "prop-types";
import styles
from "./sandboxed-codeeditor.scss";
import {
getPublicUrl,
getSandboxUrl,
getTrustedUrl
} from "./urls";
import {
base,
unbase
} from "../../../shared/templates";
import brace from 'brace';
import ACEEditorRaw from 'react-ace';
import 'brace/theme/github';
import 'brace/ext/searchbox';
import 'brace/mode/html';
import {CodeEditorSourceType} from "./sandboxed-codeeditor-shared";
import mjml2html from "mjml4-in-browser";
import juice from "juice";
@translate(null, { withRef: true })
class CodeEditorSandbox extends Component {
constructor(props) {
super(props);
let defaultSource;
if (props.sourceType === CodeEditorSourceType.MJML) {
defaultSource =
'<mjml>\n' +
' <mj-body>\n' +
' <mj-section>\n' +
' <mj-column>\n' +
' <!-- First column content -->\n' +
' </mj-column>\n' +
' <mj-column>\n' +
' <!-- Second column content -->\n' +
' </mj-column>\n' +
' </mj-section>\n' +
' </mj-body>\n' +
'</mjml>';
} else if (props.sourceType === CodeEditorSourceType.HTML) {
defaultSource =
'<!DOCTYPE html>\n' +
'<html>\n' +
'<head>\n' +
' <meta charset="UTF-8">\n' +
' <title>Title of the document</title>\n' +
'</head>\n' +
'<body>\n' +
' Content of the document......\n' +
'</body>\n' +
'</html>';
}
const trustedUrlBase = getTrustedUrl();
const sandboxUrlBase = getSandboxUrl();
const publicUrlBase = getPublicUrl();
const source = this.props.initialSource ? base(this.props.initialSource, trustedUrlBase, sandboxUrlBase, publicUrlBase) : defaultSource;
this.state = {
source,
preview: props.initialPreview
};
}
static propTypes = {
entityTypeId: PropTypes.string,
entityId: PropTypes.number,
initialSource: PropTypes.string,
sourceType: PropTypes.string,
initialPreview: PropTypes.bool
}
async exportState(method, params) {
const trustedUrlBase = getTrustedUrl();
const sandboxUrlBase = getSandboxUrl();
const publicUrlBase = getPublicUrl();
return {
source: unbase(this.state.source, trustedUrlBase, sandboxUrlBase, publicUrlBase, true)
};
}
async setPreview(method, preview) {
this.setState({
preview
});
}
componentDidMount() {
parentRPC.setMethodHandler('exportState', ::this.exportState);
parentRPC.setMethodHandler('setPreview', ::this.setPreview);
}
render() {
let previewContents;
if (this.props.sourceType === CodeEditorSourceType.MJML) {
const res = mjml2html(this.state.source);
previewContents = res.html;
} else if (this.props.sourceType === CodeEditorSourceType.HTML) {
previewContents = juice(this.state.source);
}
return (
<div className={styles.sandbox}>
<div className={this.state.preview ? styles.aceEditorWithPreview : styles.aceEditorWithoutPreview}>
<ACEEditorRaw
mode="html"
theme="github"
width="100%"
height="100%"
onChange={data => this.setState({source: data})}
fontSize={12}
showPrintMargin={false}
value={this.state.source}
tabSize={2}
setOptions={{useWorker: false}} // This disables syntax check because it does not always work well (e.g. in case of JS code in report templates)
/>
</div>
{
this.state.preview &&
<div className={styles.preview}>
<iframe src={"data:text/html;charset=utf-8," + escape(previewContents)}></iframe>
</div>
}
</div>
);
}
}
export default function() {
parentRPC.init();
ReactDOM.render(
<I18nextProvider i18n={ i18n }>
<UntrustedContentRoot render={props => <CodeEditorSandbox {...props} />} />
</I18nextProvider>,
document.getElementById('root')
);
};