react-email-builder
Version:
A simple React drag and drop email builder.
270 lines (269 loc) • 8.63 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.replaceHtmlVariables = exports.renderBlock = exports.generateMJML = exports.createBlockAttrs = exports.lines = exports.padding = exports.color = exports.px = exports.renderTag = void 0;
/**
* Render MJML tag.
*
* @param tagName The tag name. i.e. `mj-image`.
* @param options The render options.
* @returns The rendered string.
*/
function renderTag(tagName, options) {
const attrs = options?.attrs;
const children = options?.children;
let code = '<' + tagName;
if (attrs) {
Object.entries(attrs).forEach(([key, value]) => {
if (typeof value === 'string') {
code = code + ' ' + key + '=' + JSON.stringify(value);
}
});
}
code += '>';
if (children) {
if (typeof children === 'string') {
code = code + '\n' + children + '\n';
}
else if (children.length) {
code = code + '\n' + children.join('\n') + '\n';
}
}
return code + '</' + tagName + '>';
}
exports.renderTag = renderTag;
/**
* Add `px` suffix to the value. If the value is `null` or `undefined`,
* returns `null`.
*/
function px(value) {
return value == null ? null : '' + value + 'px';
}
exports.px = px;
/**
* Normalize the color value.
*/
function color(value) {
if (value != null) {
return value.startsWith('#') ? value.toUpperCase() : value;
}
return null;
}
exports.color = color;
/**
* Render padding array.
*
* @example
*
* // '10px 20px 10px 20px'
* padding([10, 20, 10, 20])
*/
function padding(value) {
if (value) {
return ('' +
(value[0] || 0) +
'px ' +
(value[1] || 0) +
'px ' +
(value[2] || 0) +
'px ' +
(value[3] || 0) +
'px');
}
return null;
}
exports.padding = padding;
/**
* Combines multiple arrays of strings into a single string, with each string
* in the arrays separated by newlines. Null or undefined inputs are ignored.
*/
function lines(...text) {
let code = '';
text.forEach((value) => {
if (Array.isArray(value)) {
code = code + (code ? '\n' : '') + value.join('\n');
}
});
return code;
}
exports.lines = lines;
/**
* Create block attrs and merge the extra attrs into it.
*/
function createBlockAttrs(block, extra) {
const style = block.style;
const padding = style.padding || [];
return {
'container-background-color': color(style.bgColor),
'padding-top': px(padding[0]),
'padding-right': px(padding[1]),
'padding-bottom': px(padding[2]),
'padding-left': px(padding[3]),
...extra
};
}
exports.createBlockAttrs = createBlockAttrs;
/**
* Generate MJML code from EmailBuilderState.
*/
function generateMJML(options) {
const sections = [];
const { state } = options;
const padding = state.style?.padding || [];
const paddingTop = padding[0];
const paddingBottom = padding[2];
const headTags = [];
options.config.blocks.forEach((block) => {
const code = block.renderMJMLHeadTags?.();
if (code) {
headTags.push(code);
}
});
if (paddingTop) {
sections.push(renderTag('mj-section', {
children: renderColumn({
children: renderTag('mj-spacer', {
attrs: { height: px(paddingTop) }
})
})
}));
}
state.blocks.forEach((block) => {
if (block.type === 'columns') {
const cols = block;
sections.push(renderSection({
block,
children: cols.attrs.columns.map((column) => {
const style = column.attrs;
const padding = style.padding || [];
return renderColumn({
attrs: {
'background-color': color(style.bgColor),
'padding-top': px(padding[0]),
'padding-right': px(padding[1]),
'padding-bottom': px(padding[2]),
'padding-left': px(padding[3])
},
children: column.blocks.map((b) => renderBlock(b, options))
});
})
}));
}
else if (block.type !== 'placeholder') {
sections.push(renderSection({
block,
children: renderColumn({
children: renderBlock(block, options)
})
}));
}
});
if (paddingBottom) {
sections.push(renderTag('mj-section', {
children: renderColumn({
children: renderTag('mj-spacer', {
attrs: { height: px(paddingBottom) }
})
})
}));
}
const textColor = color(state.style?.color || '#000000');
const fontSize = px(state.style?.fontSize ?? 14);
const fontFamily = state.style?.fontFamily ?? 'Arial, helvetica, sans-serif';
return renderTag('mjml', {
children: [
renderTag('mj-head', {
children: lines([
renderTag('mj-style', {
children: [
'html, body { color: ' +
textColor +
'; font-size: ' +
fontSize +
'; font-family: ' +
fontFamily +
'; }'
]
}),
renderTag('mj-breakpoint', { attrs: { width: '600px' } }),
renderTag('mj-attributes', {
children: [
renderTag('mj-all', {
attrs: {
padding: '0px',
color: textColor,
'font-size': fontSize,
'font-family': fontFamily
}
})
]
})
], headTags, options.extraHeadTags)
}),
renderTag('mj-body', {
children: sections,
attrs: { 'background-color': color(options.state.style?.bgColor) }
})
]
});
}
exports.generateMJML = generateMJML;
/**
* Render the block to MJML code.
*/
function renderBlock(block, options) {
const renderMJML = options.config.blocks.find((b) => b.type === block.type)?.renderMJML;
if (renderMJML) {
return renderMJML(block, options);
}
return renderTag('mj-raw', {
children: renderTag('div', {
attrs: {
style: 'padding: 10px 20px; background: #fff; color: #f00;'
},
children: 'Error: the renderMJML method of the ' +
block.type +
' block is not defined.'
})
});
}
exports.renderBlock = renderBlock;
/**
* Replace variables in the html code.
*/
function replaceHtmlVariables(html, replace) {
const needle = 'data-variable-value=';
if (replace && html && html.indexOf(needle) > -1) {
return html.replace(/(<[a-zA-Z0-9]+\s+[^>]*\bdata-variable-value=['"])\(([^)]+)\)([^>]+>)([^<]+)(<\/[^>]+>)/g, (_, openTag, varKey, attrs, placeholder, endTag) => {
return (openTag.slice(0, -(needle.length + 1)) +
attrs.slice(1) +
replace(varKey, placeholder) +
endTag);
});
}
return html;
}
exports.replaceHtmlVariables = replaceHtmlVariables;
function renderColumn(options) {
return renderTag('mj-column', options);
}
function renderSection({ block, children }) {
const attrs = {};
const style = block.style || {};
if (block.type === 'columns') {
const padding = style.padding || [];
attrs['padding-top'] = px(padding[0]);
attrs['padding-right'] = px(padding[1]);
attrs['padding-bottom'] = px(padding[2]);
attrs['padding-left'] = px(padding[3]);
}
if (style.sectionBgColor) {
attrs['background-color'] = color(style.sectionBgColor);
if (style.full !== 'no') {
attrs['full-width'] = 'full-width';
}
}
return renderTag('mj-section', {
attrs,
children
});
}