Skip to content

Commit

Permalink
perf: Update blocks to the latest version
Browse files Browse the repository at this point in the history
  • Loading branch information
wanoo21 committed May 18, 2022
1 parent 39449fe commit 5dd71a1
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 322 deletions.
1 change: 1 addition & 0 deletions .github/workflows/auto-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
branches:
- master
- beta
- alpha
- next

jobs:
Expand Down
86 changes: 32 additions & 54 deletions mjml-output/EmailTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,44 @@ import {join} from "path";

import {Section} from './Section';
import {IIPDefaultEmail} from './interfaces';
import {createBackground, createBorder, createPadding, createWidthHeight} from './utils';
import {createBackground, createPadding, createWidthHeight} from './utils';

export class EmailTemplate {
// fontsMap = new Map();
constructor(
private template: IIPDefaultEmail & { googleFonts: string[] }
) {
constructor(private template: IIPDefaultEmail & { googleFonts: string[] }) {
}

render(): string {
const {structures, general} = this.template;

return `
<mjml>
<mj-head>
<mj-title>${general.name}</mj-title>
<mj-preview>${general.previewText}</mj-preview>
${this.getUsedFonts()}
<mj-attributes>
<mj-all
padding="${createPadding(general.global.padding)}"
direction="${general.direction}"
font-family="Arial, Helvetica, sans-serif"
></mj-all>
</mj-attributes>
<mj-style>
${readFileSync(join(__dirname, 'styles.css'), {encoding: 'utf-8'})}
</mj-style>
<mj-raw>
<!--[if gte mso]>
<style type="text/css">
img { min-height: auto; }
</style>
<![endif]-->
</mj-raw>
<mj-style inline="inline">
${readFileSync(join(__dirname, 'inline-styles.css'), {encoding: 'utf-8'})}
<!--TODO extract all structures styles-->
${this.getStructuresStyles()}
.body {
padding: ${createPadding(general.padding)};
background: ${createBackground(general.background)};
${general.background.size ? `background-size: ${createWidthHeight(general.background.size)}` : ''};
}
</mj-style>
</mj-head>
<mj-body
css-class="body"
width="${createWidthHeight(general.width)}"
background-color="${general.background.color}">
${structures.map(structure => new Section(structure).render()).join('')}
</mj-body>
</mjml>
`;
<mjml>
<mj-head>
<mj-title>${general.name}</mj-title>
<mj-preview>${general.previewText}</mj-preview>
${this.getUsedFonts()}
<mj-attributes>
<mj-all padding="${createPadding(general.global.padding)}" direction="${general.direction}" font-family="Arial, Helvetica, sans-serif"></mj-all>
</mj-attributes>
<mj-style>
${readFileSync(join(__dirname, 'styles.css'), {encoding: 'utf-8'})}
</mj-style>
<mj-style inline="inline">
${readFileSync(join(__dirname, 'inline-styles.css'), {encoding: 'utf-8'})}
<!--TODO extract all structures styles-->
${this.getStructuresStyles()}
.body {
padding: ${createPadding(general.padding)};
background: ${createBackground(general.background)};
${general.background.size ? `background-size: ${createWidthHeight(general.background.size)}` : ''};
}
</mj-style>
</mj-head>
<mj-body css-class="body" width="${createWidthHeight(general.width)}" background-color="${general.background.color}">
${structures.map(structure => new Section(structure).render()).join('')}
</mj-body>
</mjml>
`;
}

private getUsedFonts() {
Expand Down Expand Up @@ -97,14 +81,8 @@ export class EmailTemplate {
}

private getStructuresStyles() {
return this.template.structures
.map(({id, options: {margin, border}}) => {
return `.${id} {
margin-top: ${margin.top}px !important;
margin-bottom: ${margin.bottom}px !important;
border: ${createBorder(border)};
}`;
})
.join('');
return this.template.structures.map(({id, options: {margin}}) => {
return `.${id} { margin-top: ${margin.top}px !important; margin-bottom: ${margin.bottom}px !important; }`;
}).join('');
}
}
204 changes: 100 additions & 104 deletions mjml-output/Section.ts
Original file line number Diff line number Diff line change
@@ -1,119 +1,115 @@
import {
IStructure,
RenderingClass,
IpBlocks,
IStructureColumnOptions
} from './interfaces';
import { Text, Image, Button, Divider, Spacer, Social } from './blocks';
import {
createWidthHeight,
createPadding,
createBorder,
defaultStructureColumnsWidth
} from './utils';
import {IpBlocks, IStructure, IStructureColumnOptions, RenderingClass} from './interfaces';
import {Button, Divider, Image, Social, Spacer, Text} from './blocks';
import {createBorder, createPadding, createWidthHeight, defaultStructureColumnsWidth} from './utils';

const defaultColumnsOptions: IStructureColumnOptions = {
background: {
color: '#cccccc'
},
border: {
width: 0,
color: '#cccccc',
radius: 0,
style: 'solid'
},
verticalAlign: 'top'
background: {
color: '#cccccc'
},
border: {
width: 0,
color: '#cccccc',
radius: 0,
style: 'solid'
},
verticalAlign: 'top'
};

export class Section implements RenderingClass {
constructor(private structure: IStructure) {}

private static getBlock(block: IpBlocks) {
switch (block.type) {
case 'text':
return new Text(block.innerText, block.options).render();
case 'image':
return new Image(block.src, block.options).render();
case 'button':
return new Button(block.innerText, block.options).render();
case 'divider':
return new Divider(block.options).render();
case 'spacer':
return new Spacer(block.options).render();
case 'social':
return new Social(block.networks, block.options).render();
constructor(private structure: IStructure) {
}
}

private getColumnWidth(index: number) {
const {
columnsWidth = defaultStructureColumnsWidth(this.structure.type)
} = this.structure.options;
const fullWidth = columnsWidth.reduce((n, fr) => n + fr, 0);
const colFr = columnsWidth[index];
return Math.round((100 * colFr) / fullWidth);
}

private createColumns() {
const {
elements,
options: { disableResponsive = false, gaps = [4, 4], columns = [] }
} = this.structure;
private static getBlock(block: IpBlocks) {
switch (block.type) {
case 'text':
return new Text(block.innerText, block.options).render();
case 'image':
return new Image(block.src, block.options).render();
case 'button':
return new Button(block.innerText, block.options).render();
case 'divider':
return new Divider(block.options).render();
case 'spacer':
return new Spacer(block.options).render();
case 'social':
return new Social(block.networks, block.options).render();
}
}

const columnsElements = elements
.map((el, index) => {
const column = (columns && columns[index]) || defaultColumnsOptions;
/**
* Limitations of background-images size and position on Outlook desktop:
* - If background-size is not specified, no-repeat will be ignored on Outlook.
* - If the specified size is a single attribute in percent, the height will be auto as in standard css.
* On outlook, the image will never overflow the element, and it will be shrinked instead of being cropped like on other clients.
*
* @version 4
* Add support for attributes:
* full-width
*/
render() {
const {type, id, options} = this.structure;
let cssClass = `${type} ${id} ip-section`;
if (options.disableResponsive) {
cssClass = `${cssClass} disable-responsive`;
}
return `
<mj-column
width="${this.getColumnWidth(index)}%"
background-color="${column.background.color}"
padding="${gaps[0]}px ${gaps[1]}px"
border="${createBorder(column.border)}"
border-radius="${column.border.radius || 0}px"
vertical-align="${column.verticalAlign}"
css-class="ip-column ${
(column.border.radius || 0) > 0 ? 'ip-border-radius' : ''
}">
${el.map(block => Section.getBlock(block)).join('')}
</mj-column>
`;
})
.join('');
<mj-section
full-width="false"
css-class="${cssClass}"
border="${createBorder(options.border)}"
border-radius="${options.border.radius}px"
text-align="center"
padding="${createPadding(options.padding)}"
background-color="${options.background.color}"
background-url="${options.background.url}"
background-repeat="${options.background.repeat}"
background-size="${
options.background.size
? createWidthHeight(options.background.size)
: 'auto'
}">
${this.createColumns()}
</mj-section>
`;
}

if (disableResponsive) {
return `<mj-group>${columnsElements}</mj-group>`;
private getColumnWidth(index: number) {
const {
columnsWidth = defaultStructureColumnsWidth(this.structure.type)
} = this.structure.options;
const fullWidth = columnsWidth.reduce((n, fr) => n + fr, 0);
const colFr = columnsWidth[index];
return Math.round((100 * colFr) / fullWidth);
}

return columnsElements;
}
private createColumns() {
const {
elements,
options: {disableResponsive = false, gaps = [4, 4], columns = []}
} = this.structure;

const columnsElements = elements.map((el, index) => {
const column = (columns && columns[index]) || defaultColumnsOptions;
return `
<mj-column
width="${this.getColumnWidth(index)}%"
background-color="${column.background.color}"
padding="${gaps[0]}px ${gaps[1]}px"
border="${createBorder(column.border)}"
border-radius="${column.border.radius || 0}px"
vertical-align="${column.verticalAlign}"
css-class="ip-column ${
(column.border.radius || 0) > 0 ? 'ip-border-radius' : ''
}">
${el.map(block => Section.getBlock(block)).join('')}
</mj-column>
`;
}).join('');

if (disableResponsive) {
return `<mj-group>${columnsElements}</mj-group>`;
}

/**
* @version 4
* Add support for full-width
*/
render() {
const { type, id, options } = this.structure;
let cssClass = `${type} ${id} ip-section`;
if (options.disableResponsive) {
cssClass = `${cssClass} disable-responsive`;
return columnsElements;
}
return `
<mj-section
css-class="${cssClass}"
full-width="none"
border-radius="${options.border.radius}px"
text-align="center"
padding="${createPadding(options.padding)}"
background-color="${options.background.color}"
background-url="${options.background.url}"
background-repeat="${options.background.repeat}"
background-size="${
options.background.size
? createWidthHeight(options.background.size)
: 'auto'
}">
${this.createColumns()}
</mj-section>
`;
}
}
Loading

0 comments on commit 5dd71a1

Please sign in to comment.