Added ability to make a conditional block in MJML Mosaico.
Mosaico switched from master to v0.17.5 Added workaround for Chrome - after save, images in Mosaico disappear
This commit is contained in:
parent
a527b80291
commit
2e9d44c705
12 changed files with 1483 additions and 1304 deletions
|
@ -57,7 +57,7 @@ class MosaicoSandbox extends Component {
|
|||
...
|
||||
</div>
|
||||
*/
|
||||
let html = this.viewModel.exportHTML();
|
||||
let html = this.viewModel.export();
|
||||
html = juice(html);
|
||||
|
||||
return {
|
||||
|
@ -86,6 +86,18 @@ class MosaicoSandbox extends Component {
|
|||
viewModel.originalExportHTML = viewModel.exportHTML;
|
||||
viewModel.exportHTML = () => {
|
||||
let html = viewModel.originalExportHTML();
|
||||
|
||||
// Chrome workaround begin -----------------------------------------------------------------------------------
|
||||
// Chrome v. 74 (and likely other versions too) has problem with how KO sets data during export.
|
||||
// As the result, the images that have been in the template from previous editing (i.e. before page refresh)
|
||||
// get lost. The code below refreshes the KO binding, thus effectively reloading the images.
|
||||
const isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);
|
||||
if (isChrome) {
|
||||
ko.cleanNode(document.body);
|
||||
ko.applyBindings(viewModel, document.body);
|
||||
}
|
||||
// Chrome workaround end -------------------------------------------------------------------------------------
|
||||
|
||||
for (const portRender of window.mosaicoHTMLPostRenderers) {
|
||||
html = postRender(html);
|
||||
}
|
||||
|
|
|
@ -199,8 +199,14 @@ export default class CUD extends Component {
|
|||
{isEdit && typeKey && this.templateTypes[typeKey].getForm(this)}
|
||||
|
||||
<ButtonRow>
|
||||
<Button type="submit" className="btn-primary" icon="check" label={t('save')}/>
|
||||
<Button type="submit" className="btn-primary" icon="check" label={t('saveAndLeave')} onClickAsync={async () => await this.submitHandler(true)}/>
|
||||
{isEdit ?
|
||||
<>
|
||||
<Button type="submit" className="btn-primary" icon="check" label={t('save')}/>
|
||||
<Button type="submit" className="btn-primary" icon="check" label={t('saveAndLeave')} onClickAsync={async () => await this.submitHandler(true)}/>
|
||||
</>
|
||||
:
|
||||
<Button type="submit" className="btn-primary" icon="check" label={t('saveAndEditContent')}/>
|
||||
}
|
||||
{canDelete && <LinkButton className="btn-danger" icon="trash-alt" label={t('delete')} to={`/templates/mosaico/${this.props.entity.id}/delete`}/>}
|
||||
{isEdit && typeKey && this.templateTypes[typeKey].getButtons(this)}
|
||||
</ButtonRow>
|
||||
|
|
|
@ -26,7 +26,7 @@ function getParent() {
|
|||
}
|
||||
|
||||
|
||||
function handleMosaicoEditable(block, src) {
|
||||
function handleMosaicoAttributes(block, src) {
|
||||
let newSrc = src;
|
||||
let offset = 0;
|
||||
|
||||
|
@ -43,13 +43,15 @@ function handleMosaicoEditable(block, src) {
|
|||
|
||||
let newFragment = tagStr;
|
||||
|
||||
|
||||
for (const attrMatch of attrsMatches) {
|
||||
if (attrMatch[1] === 'mj-mosaico-editable') {
|
||||
const propertyId = attrMatch[2];
|
||||
block.addMosaicoProperty(propertyId);
|
||||
|
||||
newFragment += ` data-ko-editable="${propertyId}"`;
|
||||
} else if (attrMatch[1] === 'mj-mosaico-display') {
|
||||
const propertyId = attrMatch[2];
|
||||
block.addMosaicoProperty(propertyId);
|
||||
newFragment += ` data-ko-display="${propertyId}"`;
|
||||
} else {
|
||||
newFragment += ` ${attrMatch[0]}`;
|
||||
}
|
||||
|
@ -75,35 +77,39 @@ function handleMosaicoEditable(block, src) {
|
|||
|
||||
|
||||
class MjMosaicoProperty extends HeadComponent {
|
||||
static endingTag = true
|
||||
static endingTag = true;
|
||||
|
||||
static allowedAttributes = {
|
||||
'property-id': 'string',
|
||||
label: 'string',
|
||||
type: 'enum(image,link)'
|
||||
}
|
||||
type: 'enum(image,link,visible)'
|
||||
};
|
||||
|
||||
handler() {
|
||||
const { add } = this.context;
|
||||
|
||||
let properties = null;
|
||||
let extend = null;
|
||||
|
||||
const type = this.getAttribute('type');
|
||||
if (type === 'image') {
|
||||
properties = 'src url alt';
|
||||
} else if (type === 'link') {
|
||||
properties = 'text url';
|
||||
} else if (type === 'visible') {
|
||||
extend = 'visible';
|
||||
}
|
||||
|
||||
const propertiesStr = properties ? ` properties: ${properties}` : '';
|
||||
const extendStr = extend ? ` extend: ${extend}` : '';
|
||||
|
||||
add('style', ` @supports -ko-blockdefs { ${this.getAttribute('property-id')} { label: ${this.getAttribute('label')};${propertiesStr} } }`);
|
||||
add('style', ` @supports -ko-blockdefs { ${this.getAttribute('property-id')} { label: ${this.getAttribute('label')};${propertiesStr}${extendStr} } }`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MjMosaicoContainer extends BodyComponent {
|
||||
static endingTag = false
|
||||
static endingTag = false;
|
||||
|
||||
render() {
|
||||
return `
|
||||
|
@ -114,6 +120,90 @@ class MjMosaicoContainer extends BodyComponent {
|
|||
}
|
||||
}
|
||||
|
||||
class MjMosaicoConditionalDisplay extends BodyComponent {
|
||||
constructor(initialDatas = {}) {
|
||||
super(initialDatas);
|
||||
|
||||
const propertyId = this.getAttribute('property-id');
|
||||
this.propertyId = propertyId || `display_${getId()}`;
|
||||
|
||||
const parentBlock = getParent();
|
||||
if (parentBlock) {
|
||||
parentBlock.addMosaicoProperty(this.propertyId);
|
||||
}
|
||||
}
|
||||
|
||||
componentHeadStyle = breakpoint => {
|
||||
const label = this.getAttribute('label');
|
||||
if (label) {
|
||||
return `
|
||||
@supports -ko-blockdefs {
|
||||
${this.propertyId} { label: ${label}; extend: visible }
|
||||
}
|
||||
`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
static endingTag = false;
|
||||
|
||||
static allowedAttributes = {
|
||||
'property-id': 'string',
|
||||
'label': 'string'
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
return `
|
||||
<div
|
||||
${this.htmlAttributes({
|
||||
"data-ko-display": this.propertyId,
|
||||
class: this.getAttribute('css-class'),
|
||||
'data-ko-block': this.blockId
|
||||
})}
|
||||
>
|
||||
<table
|
||||
${this.htmlAttributes({
|
||||
border: '0',
|
||||
cellpadding: '0',
|
||||
cellspacing: '0',
|
||||
role: 'presentation',
|
||||
style: 'table',
|
||||
width: '100%',
|
||||
})}
|
||||
>
|
||||
${this.renderChildren(children, {
|
||||
renderer: component => component.constructor.isRawElement() ? component.render() : `
|
||||
<tr>
|
||||
<td
|
||||
${component.htmlAttributes({
|
||||
align: component.getAttribute('align'),
|
||||
'vertical-align': component.getAttribute('vertical-align'),
|
||||
class: component.getAttribute('css-class'),
|
||||
style: {
|
||||
background: component.getAttribute('container-background-color'),
|
||||
'font-size': '0px',
|
||||
padding: component.getAttribute('padding'),
|
||||
'padding-top': component.getAttribute('padding-top'),
|
||||
'padding-right': component.getAttribute('padding-right'),
|
||||
'padding-bottom': component.getAttribute('padding-bottom'),
|
||||
'padding-left': component.getAttribute('padding-left'),
|
||||
'word-break': 'break-word',
|
||||
},
|
||||
})}
|
||||
>
|
||||
${component.render()}
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
})}
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MjMosaicoBlock extends BodyComponent {
|
||||
constructor(initialDatas = {}) {
|
||||
|
@ -132,14 +222,14 @@ class MjMosaicoBlock extends BodyComponent {
|
|||
${this.blockId} { label: ${this.getAttribute('label')}${propertiesOut} }
|
||||
}
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
static endingTag = false
|
||||
static endingTag = false;
|
||||
|
||||
static allowedAttributes = {
|
||||
'block-id': 'string',
|
||||
'label': 'string'
|
||||
}
|
||||
};
|
||||
|
||||
addMosaicoProperty(property) {
|
||||
this.mosaicoProperties.push(property);
|
||||
|
@ -159,7 +249,7 @@ class MjMosaicoBlock extends BodyComponent {
|
|||
`;
|
||||
popParent();
|
||||
|
||||
return handleMosaicoEditable(this, result);
|
||||
return handleMosaicoAttributes(this, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,14 +271,14 @@ class MjMosaicoInnerBlock extends BodyComponent {
|
|||
${this.blockId} { label: ${this.getAttribute('label')}${propertiesOut} }
|
||||
}
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
static endingTag = false
|
||||
static endingTag = false;
|
||||
|
||||
static allowedAttributes = {
|
||||
'block-id': 'string',
|
||||
'label': 'string'
|
||||
}
|
||||
};
|
||||
|
||||
addMosaicoProperty(property) {
|
||||
this.mosaicoProperties.push(property);
|
||||
|
@ -238,14 +328,14 @@ class MjMosaicoInnerBlock extends BodyComponent {
|
|||
${component.render()}
|
||||
</td>
|
||||
</tr>
|
||||
`,
|
||||
`
|
||||
})}
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
popParent();
|
||||
|
||||
return handleMosaicoEditable(this, result);
|
||||
return handleMosaicoAttributes(this, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,7 +354,7 @@ class MjMosaicoImage extends BodyComponent {
|
|||
}
|
||||
}
|
||||
|
||||
static tagOmission = true
|
||||
static tagOmission = true;
|
||||
|
||||
static allowedAttributes = {
|
||||
'property-id': 'string',
|
||||
|
@ -292,7 +382,7 @@ class MjMosaicoImage extends BodyComponent {
|
|||
target: 'string',
|
||||
width: 'unit(px)',
|
||||
height: 'unit(px)',
|
||||
}
|
||||
};
|
||||
|
||||
static defaultAttributes = {
|
||||
align: 'center',
|
||||
|
@ -302,7 +392,7 @@ class MjMosaicoImage extends BodyComponent {
|
|||
target: '_blank',
|
||||
'placeholder-height': '400',
|
||||
'href-editable': false
|
||||
}
|
||||
};
|
||||
|
||||
getStyles() {
|
||||
const width = this.getContentWidth();
|
||||
|
@ -453,7 +543,7 @@ class MjMosaicoButton extends BodyComponent {
|
|||
}
|
||||
}
|
||||
|
||||
static endingTag = true
|
||||
static endingTag = true;
|
||||
|
||||
static allowedAttributes = {
|
||||
'property-id': 'string',
|
||||
|
@ -487,7 +577,7 @@ class MjMosaicoButton extends BodyComponent {
|
|||
'vertical-align': 'enum(top,bottom,middle)',
|
||||
'text-align': 'enum(left,right,center)',
|
||||
width: 'unit(px,%)',
|
||||
}
|
||||
};
|
||||
|
||||
static defaultAttributes = {
|
||||
align: 'center',
|
||||
|
@ -505,7 +595,7 @@ class MjMosaicoButton extends BodyComponent {
|
|||
'text-decoration': 'none',
|
||||
'text-transform': 'none',
|
||||
'vertical-align': 'middle',
|
||||
}
|
||||
};
|
||||
|
||||
getStyles() {
|
||||
return {
|
||||
|
@ -591,6 +681,7 @@ class MjMosaicoButton extends BodyComponent {
|
|||
const mjmlInstance = new MJML();
|
||||
|
||||
mjmlInstance.registerComponent(MjMosaicoContainer);
|
||||
mjmlInstance.registerComponent(MjMosaicoConditionalDisplay);
|
||||
mjmlInstance.registerComponent(MjMosaicoBlock);
|
||||
mjmlInstance.registerComponent(MjMosaicoInnerBlock);
|
||||
mjmlInstance.registerComponent(MjMosaicoImage);
|
||||
|
@ -601,8 +692,13 @@ mjmlInstance.registerDependencies({
|
|||
'mj-mosaico-container': ['mj-mosaico-block', 'mj-mosaico-inner-block'],
|
||||
'mj-body': ['mj-mosaico-container', 'mj-mosaico-block'],
|
||||
'mj-section': ['mj-mosaico-container', 'mj-mosaico-block'],
|
||||
'mj-column': ['mj-mosaico-container', 'mj-mosaico-inner-block', 'mj-mosaico-image', 'mj-mosaico-button'],
|
||||
'mj-column': ['mj-mosaico-container', 'mj-mosaico-inner-block', 'mj-mosaico-image', 'mj-mosaico-button', 'mj-mosaico-conditional-display'],
|
||||
'mj-mosaico-block': ['mj-section', 'mj-column'],
|
||||
'mj-mosaico-conditional-display': [
|
||||
'mj-mosaico-image', 'mj-mosaico-button',
|
||||
'mj-accordion', 'mj-button', 'mj-carousel', 'mj-divider', 'mj-html', 'mj-image', 'mj-invoice', 'mj-list',
|
||||
'mj-location', 'mj-raw', 'mj-social', 'mj-spacer', 'mj-table', 'mj-text', 'mj-navbar'
|
||||
],
|
||||
'mj-mosaico-inner-block': [
|
||||
'mj-mosaico-image', 'mj-mosaico-button',
|
||||
'mj-accordion', 'mj-button', 'mj-carousel', 'mj-divider', 'mj-html', 'mj-image', 'mj-invoice', 'mj-list',
|
||||
|
@ -614,6 +710,8 @@ mjmlInstance.registerDependencies({
|
|||
mjmlInstance.addToHeader(`
|
||||
<style type="text/css">
|
||||
@supports -ko-blockdefs {
|
||||
visible { label: Visible?; widget: boolean }
|
||||
color { label: Color; widget: color }
|
||||
text { label: Paragraph; widget: text }
|
||||
image { label: Image; properties: src url alt }
|
||||
link { label: Link; properties: text url }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue