Save button for template editors

This commit is contained in:
Tomas Bures 2018-11-22 20:53:44 +01:00
parent 3bb235a585
commit bd20072455
8 changed files with 73 additions and 17 deletions

View file

@ -63,15 +63,17 @@ export default class CustomContent extends Component {
entity: PropTypes.object entity: PropTypes.object
} }
componentDidMount() { loadFromEntityMutator(data) {
this.getFormValuesFromEntity(this.props.entity, data => { data.data_sourceCustom_type = data.data.sourceCustom.type;
data.data_sourceCustom_type = data.data.sourceCustom.type; data.data_sourceCustom_data = data.data.sourceCustom.data;
data.data_sourceCustom_data = data.data.sourceCustom.data; data.data_sourceCustom_html = data.data.sourceCustom.html;
data.data_sourceCustom_html = data.data.sourceCustom.html; data.data_sourceCustom_text = data.data.sourceCustom.text;
data.data_sourceCustom_text = data.data.sourceCustom.text;
this.templateTypes[data.data.sourceCustom.type].afterLoad(data); this.templateTypes[data.data.sourceCustom.type].afterLoad(data);
}); }
componentDidMount() {
this.getFormValuesFromEntity(this.props.entity, data => this.loadFromEntityMutator(data));
} }
localValidateFormValues(state) { localValidateFormValues(state) {
@ -84,7 +86,15 @@ export default class CustomContent extends Component {
} }
} }
async save() {
await this.doSave(true);
}
async submitHandler() { async submitHandler() {
await this.doSave(false);
}
async doSave(stayOnPage) {
const t = this.props.t; const t = this.props.t;
const customTemplateTypeKey = this.getFormValue('data_sourceCustom_type'); const customTemplateTypeKey = this.getFormValue('data_sourceCustom_type');
@ -115,10 +125,14 @@ export default class CustomContent extends Component {
}); });
if (submitResponse) { if (submitResponse) {
if (this.props.entity) { if (stayOnPage) {
this.navigateToWithFlashMessage('/campaigns', 'success', t('campaignSaved')); await this.getFormValuesFromURL(`rest/campaigns-content/${this.props.entity.id}`, data => this.loadFromEntityMutator(data));
this.enableForm();
this.clearFormStatusMessage();
this.setFlashMessage('success', t('campaignSaved'));
} else { } else {
this.navigateToWithFlashMessage(`/campaigns/${submitResponse}/edit`, 'success', t('campaignSaved')); this.navigateToWithFlashMessage('/campaigns', 'success', t('campaignSaved'));
} }
} else { } else {
this.enableForm(); this.enableForm();

View file

@ -31,6 +31,8 @@ export class CKEditorHost extends Component {
entity: PropTypes.object, entity: PropTypes.object,
initialSource: PropTypes.string, initialSource: PropTypes.string,
title: PropTypes.string, title: PropTypes.string,
onSave: PropTypes.func,
canSave: PropTypes.bool,
onTestSend: PropTypes.func, onTestSend: PropTypes.func,
onFullscreenAsync: PropTypes.func onFullscreenAsync: PropTypes.func
} }
@ -91,6 +93,7 @@ export class CKEditorHost extends Component {
<div className={styles.title}>{this.props.title}</div> <div className={styles.title}>{this.props.title}</div>
<a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a> <a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a>
<a className={styles.btn} onClick={this.props.onTestSend}><Icon icon="send"/></a> <a className={styles.btn} onClick={this.props.onTestSend}><Icon icon="send"/></a>
{this.props.canSave ? <a className={styles.btn} onClick={this.props.onSave}><Icon icon="floppy-disk"/></a> : <span className={styles.btnDisabled}><Icon icon="floppy-disk"/></span>}
</div> </div>
<UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="ckeditor/editor" tokenMethod="ckeditor" tokenParams={editorData}/> <UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="ckeditor/editor" tokenMethod="ckeditor" tokenParams={editorData}/>
</div> </div>

View file

@ -91,6 +91,9 @@ class CodeEditorSandbox extends Component {
this.refreshHandler = ::this.refresh; this.refreshHandler = ::this.refresh;
this.refreshTimeoutId = null; this.refreshTimeoutId = null;
this.onMessageFromPreviewHandler = ::this.onMessageFromPreview;
this.previewScroll = {x: 0, y: 0};
} }
static propTypes = { static propTypes = {
@ -128,6 +131,8 @@ class CodeEditorSandbox extends Component {
parentRPC.setMethodHandler('exportState', ::this.exportState); parentRPC.setMethodHandler('exportState', ::this.exportState);
parentRPC.setMethodHandler('setPreview', ::this.setPreview); parentRPC.setMethodHandler('setPreview', ::this.setPreview);
parentRPC.setMethodHandler('setWrap', ::this.setWrap); parentRPC.setMethodHandler('setWrap', ::this.setWrap);
window.addEventListener('message', this.onMessageFromPreviewHandler, false);
} }
componentWillUnmount() { componentWillUnmount() {
@ -156,6 +161,12 @@ class CodeEditorSandbox extends Component {
} }
} }
onMessageFromPreview(evt) {
if (evt.data.type === 'scroll') {
this.previewScroll = evt.data.data;
}
}
refresh() { refresh() {
this.refreshTimeoutId = null; this.refreshTimeoutId = null;
@ -165,6 +176,16 @@ class CodeEditorSandbox extends Component {
} }
render() { render() {
const previewScript =
'(function() {\n' +
' function reportScroll() { window.parent.postMessage({type: \'scroll\', data: {x: window.scrollX, y: window.scrollY}}, \'*\'); }\n' +
' reportScroll();\n' +
' window.addEventListener(\'scroll\', reportScroll);\n' +
' window.addEventListener(\'load\', function(evt) { window.scrollTo(' + this.previewScroll.x + ',' + this.previewScroll.y +'); });\n' +
'})();\n';
const previewContents = this.state.previewContents.replace(/<\s*head\s*>/i, `<head><script>${previewScript}</script>`);
return ( return (
<div className={styles.sandbox}> <div className={styles.sandbox}>
<div className={this.state.preview ? styles.aceEditorWithPreview : styles.aceEditorWithoutPreview}> <div className={this.state.preview ? styles.aceEditorWithPreview : styles.aceEditorWithoutPreview}>
@ -185,7 +206,7 @@ class CodeEditorSandbox extends Component {
{ {
this.state.preview && this.state.preview &&
<div className={styles.preview}> <div className={styles.preview}>
<iframe ref={node => this.previewNode = node} src={"data:text/html;charset=utf-8," + escape(this.state.previewContents)}></iframe> <iframe ref={node => this.previewNode = node} src={"data:text/html;charset=utf-8," + escape(previewContents)}></iframe>
</div> </div>
} }
</div> </div>

View file

@ -29,8 +29,9 @@ export class CodeEditorHost extends Component {
initialSource: PropTypes.string, initialSource: PropTypes.string,
sourceType: PropTypes.string, sourceType: PropTypes.string,
title: PropTypes.string, title: PropTypes.string,
onTestSend: PropTypes.func,
onSave: PropTypes.func, onSave: PropTypes.func,
canSave: PropTypes.bool,
onTestSend: PropTypes.func,
onFullscreenAsync: PropTypes.func onFullscreenAsync: PropTypes.func
} }

View file

@ -28,6 +28,8 @@ export class GrapesJSHost extends Component {
initialStyle: PropTypes.string, initialStyle: PropTypes.string,
sourceType: PropTypes.string, sourceType: PropTypes.string,
title: PropTypes.string, title: PropTypes.string,
onSave: PropTypes.func,
canSave: PropTypes.bool,
onTestSend: PropTypes.func, onTestSend: PropTypes.func,
onFullscreenAsync: PropTypes.func onFullscreenAsync: PropTypes.func
} }
@ -67,6 +69,7 @@ export class GrapesJSHost extends Component {
<div className={styles.title}>{this.props.title}</div> <div className={styles.title}>{this.props.title}</div>
<a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a> <a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a>
<a className={styles.btn} onClick={this.props.onTestSend}><Icon icon="send"/></a> <a className={styles.btn} onClick={this.props.onTestSend}><Icon icon="send"/></a>
{this.props.canSave ? <a className={styles.btn} onClick={this.props.onSave}><Icon icon="floppy-disk"/></a> : <span className={styles.btnDisabled}><Icon icon="floppy-disk"/></span>}
</div> </div>
<UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="grapesjs/editor" tokenMethod="grapesjs" tokenParams={tokenData}/> <UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="grapesjs/editor" tokenMethod="grapesjs" tokenParams={tokenData}/>
</div> </div>

View file

@ -26,6 +26,8 @@ export class MosaicoHost extends Component {
entityTypeId: PropTypes.string, entityTypeId: PropTypes.string,
entity: PropTypes.object, entity: PropTypes.object,
title: PropTypes.string, title: PropTypes.string,
onSave: PropTypes.func,
canSave: PropTypes.bool,
onTestSend: PropTypes.func, onTestSend: PropTypes.func,
onFullscreenAsync: PropTypes.func, onFullscreenAsync: PropTypes.func,
templateId: PropTypes.number, templateId: PropTypes.number,
@ -70,6 +72,7 @@ export class MosaicoHost extends Component {
<div className={styles.title}>{this.props.title}</div> <div className={styles.title}>{this.props.title}</div>
<a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a> <a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a>
<a className={styles.btn} onClick={this.props.onTestSend}><Icon icon="send"/></a> <a className={styles.btn} onClick={this.props.onTestSend}><Icon icon="send"/></a>
{this.props.canSave ? <a className={styles.btn} onClick={this.props.onSave}><Icon icon="floppy-disk"/></a> : <span className={styles.btnDisabled}><Icon icon="floppy-disk"/></span>}
</div> </div>
<UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="mosaico/editor" tokenMethod="mosaico" tokenParams={tokenData}/> <UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="mosaico/editor" tokenMethod="mosaico" tokenParams={tokenData}/>
</div> </div>

View file

@ -52,7 +52,7 @@ export default class CUD extends Component {
this.state = { this.state = {
showMergeTagReference: false, showMergeTagReference: false,
elementInFullscreen: false, elementInFullscreen: false,
showTestSendModal: false showTestSendModal: false,
}; };
this.initForm({ this.initForm({
@ -76,11 +76,13 @@ export default class CUD extends Component {
} }
} }
loadFromEntityMutator(data) {
this.templateTypes[data.type].afterLoad(data);
}
componentDidMount() { componentDidMount() {
if (this.props.entity) { if (this.props.entity) {
this.getFormValuesFromEntity(this.props.entity, data => { this.getFormValuesFromEntity(this.props.entity, data => this.loadFromEntityMutator(data));
this.templateTypes[data.type].afterLoad(data);
});
} else { } else {
this.populateFormValues({ this.populateFormValues({
name: '', name: '',
@ -146,6 +148,7 @@ export default class CUD extends Component {
if (submitResponse) { if (submitResponse) {
if (stayOnPage) { if (stayOnPage) {
await this.getFormValuesFromURL(`rest/templates/${this.props.entity.id}`, data => this.loadFromEntityMutator(data));
this.enableForm(); this.enableForm();
this.clearFormStatusMessage(); this.clearFormStatusMessage();
this.setFlashMessage('success', t('templateSaved')); this.setFlashMessage('success', t('templateSaved'));

View file

@ -121,6 +121,8 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
templateId={owner.getFormValue(prefix + 'mosaicoTemplate')} templateId={owner.getFormValue(prefix + 'mosaicoTemplate')}
entityTypeId={entityTypeId} entityTypeId={entityTypeId}
title={t('mosaicoTemplateDesigner')} title={t('mosaicoTemplateDesigner')}
onSave={::owner.save}
canSave={owner.isFormWithoutErrors()}
onTestSend={::owner.showTestSendModal} onTestSend={::owner.showTestSendModal}
onFullscreenAsync={::owner.setElementInFullscreen} onFullscreenAsync={::owner.setElementInFullscreen}
/> />
@ -194,6 +196,8 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
templatePath={getSandboxUrl(`static/mosaico/templates/${owner.getFormValue(prefix + 'mosaicoFsTemplate')}/index.html`)} templatePath={getSandboxUrl(`static/mosaico/templates/${owner.getFormValue(prefix + 'mosaicoFsTemplate')}/index.html`)}
entityTypeId={entityTypeId} entityTypeId={entityTypeId}
title={t('mosaicoTemplateDesigner')} title={t('mosaicoTemplateDesigner')}
onSave={::owner.save}
canSave={owner.isFormWithoutErrors()}
onTestSend={::owner.showTestSendModal} onTestSend={::owner.showTestSendModal}
onFullscreenAsync={::owner.setElementInFullscreen} onFullscreenAsync={::owner.setElementInFullscreen}
/> />
@ -270,6 +274,8 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
initialStyle={owner.getFormValue(prefix + 'grapesJSData').style} initialStyle={owner.getFormValue(prefix + 'grapesJSData').style}
sourceType={owner.getFormValue(prefix + 'grapesJSSourceType')} sourceType={owner.getFormValue(prefix + 'grapesJSSourceType')}
title={t('grapesJsTemplateDesigner')} title={t('grapesJsTemplateDesigner')}
onSave={::owner.save}
canSave={owner.isFormWithoutErrors()}
onTestSend={::owner.showTestSendModal} onTestSend={::owner.showTestSendModal}
onFullscreenAsync={::owner.setElementInFullscreen} onFullscreenAsync={::owner.setElementInFullscreen}
/> />
@ -322,6 +328,8 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
initialSource={owner.getFormValue(prefix + 'ckeditor4Data').source} initialSource={owner.getFormValue(prefix + 'ckeditor4Data').source}
entityTypeId={entityTypeId} entityTypeId={entityTypeId}
title={t('ckEditor4TemplateDesigner')} title={t('ckEditor4TemplateDesigner')}
onSave={::owner.save}
canSave={owner.isFormWithoutErrors()}
onTestSend={::owner.showTestSendModal} onTestSend={::owner.showTestSendModal}
onFullscreenAsync={::owner.setElementInFullscreen} onFullscreenAsync={::owner.setElementInFullscreen}
/> />