UNPKG

react-email-builder

Version:
270 lines (269 loc) 8.63 kB
"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 }); }