email-builder-utils 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convertJsonToHtml.d.ts","sourceRoot":"","sources":["../../src/utils/convertJsonToHtml.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,GAAI,UAAU,GAAG,
|
|
1
|
+
{"version":3,"file":"convertJsonToHtml.d.ts","sourceRoot":"","sources":["../../src/utils/convertJsonToHtml.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,GAAI,UAAU,GAAG,WA6B9C,CAAA"}
|
|
@@ -4,7 +4,7 @@ exports.convertJsonToHtml = void 0;
|
|
|
4
4
|
const jsonToHTML_1 = require("./jsonToHTML");
|
|
5
5
|
const convertJsonToHtml = (jsonData) => {
|
|
6
6
|
const rootData = jsonData?.root?.data;
|
|
7
|
-
const blocksHtml = rootData?.childrenIds.map((childId) => (0, jsonToHTML_1.convertToHtml)(jsonData[childId], jsonData)).join("");
|
|
7
|
+
const blocksHtml = rootData?.childrenIds.map((childId) => (0, jsonToHTML_1.convertToHtml)(jsonData[childId], jsonData, 600)).join("");
|
|
8
8
|
const rawHtml = `
|
|
9
9
|
<!DOCTYPE html>
|
|
10
10
|
<html lang="en">
|
|
@@ -13,23 +13,14 @@ const convertJsonToHtml = (jsonData) => {
|
|
|
13
13
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
14
14
|
<title>Email Layout</title>
|
|
15
15
|
<style>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
max-width: 360px !important;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
@media screen and (min-width: 601px) {
|
|
24
|
-
.ebr-table-wrapper {
|
|
25
|
-
width: 600px !important;
|
|
26
|
-
max-width: 600px !important;
|
|
27
|
-
}
|
|
16
|
+
.responsive-table {
|
|
17
|
+
width: 100%;
|
|
18
|
+
max-width: 600px;
|
|
28
19
|
}
|
|
29
20
|
</style>
|
|
30
21
|
</head>
|
|
31
22
|
<body>
|
|
32
|
-
<table class="
|
|
23
|
+
<table class="responsive-table" style="font-family:${rootData.style?.fontFamily}; margin:0 auto; background-color:${rootData.style?.canvasColor}; color:${rootData.style?.textColor}; ${jsonToHTML_1.tableCommonStyle}">
|
|
33
24
|
<tbody>
|
|
34
25
|
<tr>
|
|
35
26
|
<td style="padding:0;">${blocksHtml}</td>
|
|
@@ -17,6 +17,6 @@ interface IBlockData {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
export declare const tableCommonStyle = "border-collapse:collapse; table-layout:fixed;";
|
|
20
|
-
export declare function convertToHtml(blockData: IBlockData, rootData: any): string
|
|
20
|
+
export declare function convertToHtml(blockData: IBlockData, rootData: any, cellWidthInPx: number): Promise<string>;
|
|
21
21
|
export {};
|
|
22
22
|
//# sourceMappingURL=jsonToHTML.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonToHTML.d.ts","sourceRoot":"","sources":["../../src/utils/jsonToHTML.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"jsonToHTML.d.ts","sourceRoot":"","sources":["../../src/utils/jsonToHTML.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AASrC,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE;QACJ,KAAK,EAAE,cAAc,CAAC;QACtB,KAAK,EAAE,GAAG,CAAC;QACX,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;KAC7B,CAAC;CACH;AASD,eAAO,MAAM,gBAAgB,kDAAkD,CAAC;AAqDhF,wBAAsB,aAAa,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAiB9F"}
|
package/dist/utils/jsonToHTML.js
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.tableCommonStyle = void 0;
|
|
4
4
|
exports.convertToHtml = convertToHtml;
|
|
5
|
+
const jimp_1 = require("jimp");
|
|
5
6
|
const types_1 = require("../types");
|
|
6
|
-
const addPxToAttributes = ["
|
|
7
|
+
const addPxToAttributes = ["fontSize", "lineHeight"];
|
|
8
|
+
const addPxOrPerToAttributes = ["width", "height"];
|
|
9
|
+
const allPxAttributes = [...addPxToAttributes, ...addPxOrPerToAttributes];
|
|
7
10
|
exports.tableCommonStyle = "border-collapse:collapse; table-layout:fixed;";
|
|
8
11
|
function cleanJson(obj) {
|
|
9
12
|
if (typeof obj !== "object" || obj === null)
|
|
@@ -25,10 +28,11 @@ function jsonToPlainString(obj) {
|
|
|
25
28
|
.map(([key, value]) => `${key}:${jsonToPlainString(value)}; `)
|
|
26
29
|
.join("");
|
|
27
30
|
}
|
|
28
|
-
function buildStyles(style) {
|
|
31
|
+
function buildStyles(style, { pxChanges, perChanges }) {
|
|
29
32
|
const stylesObj = {};
|
|
30
33
|
Object.entries(style).forEach(([key, value]) => {
|
|
31
|
-
const appendPx =
|
|
34
|
+
const appendPx = pxChanges.includes(key);
|
|
35
|
+
const appendPer = perChanges.includes(key);
|
|
32
36
|
if (value === undefined || value === null || value === "")
|
|
33
37
|
return null;
|
|
34
38
|
if ((key === "padding" || key === "buttonPadding") && typeof value === "object" && value !== null) {
|
|
@@ -36,20 +40,29 @@ function buildStyles(style) {
|
|
|
36
40
|
value = `${padding.top}px ${padding.right}px ${padding.bottom}px ${padding.left}px`;
|
|
37
41
|
}
|
|
38
42
|
const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
39
|
-
|
|
43
|
+
if (appendPx) {
|
|
44
|
+
stylesObj[cssKey] = `${value}px`;
|
|
45
|
+
}
|
|
46
|
+
else if (appendPer) {
|
|
47
|
+
stylesObj[cssKey] = `${value}%`;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
stylesObj[cssKey] = value;
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
40
53
|
});
|
|
41
54
|
return jsonToPlainString(cleanJson(stylesObj)).trim();
|
|
42
55
|
}
|
|
43
|
-
function convertToHtml(blockData, rootData) {
|
|
56
|
+
async function convertToHtml(blockData, rootData, cellWidthInPx) {
|
|
44
57
|
switch (blockData.type) {
|
|
45
58
|
case types_1.BlockType.TEXT:
|
|
46
59
|
return convertTextBlock(blockData);
|
|
47
60
|
case types_1.BlockType.IMAGE:
|
|
48
|
-
return convertImageBlock(blockData);
|
|
61
|
+
return await convertImageBlock(blockData, cellWidthInPx);
|
|
49
62
|
case types_1.BlockType.BUTTON:
|
|
50
63
|
return convertButtonBlock(blockData);
|
|
51
64
|
case types_1.BlockType.GRID:
|
|
52
|
-
return convertGridBlock(blockData, rootData);
|
|
65
|
+
return await convertGridBlock(blockData, rootData, cellWidthInPx);
|
|
53
66
|
case types_1.BlockType.DIVIDER:
|
|
54
67
|
return convertDividerBlockToHtml(blockData);
|
|
55
68
|
case types_1.BlockType.SPACER:
|
|
@@ -66,45 +79,54 @@ function appendOutlookSupport(content, contentStyle) {
|
|
|
66
79
|
function convertDividerBlockToHtml(blockData) {
|
|
67
80
|
const { style } = blockData.data;
|
|
68
81
|
const { thickness, dividerColor, ...rest } = style;
|
|
69
|
-
const convertedStyle = buildStyles(rest);
|
|
82
|
+
const convertedStyle = buildStyles(rest, { perChanges: [], pxChanges: allPxAttributes });
|
|
70
83
|
return appendOutlookSupport(`<hr style="height:${thickness}px; background-color: ${dividerColor};" />`, convertedStyle);
|
|
71
84
|
}
|
|
72
85
|
function convertSpacerBlockToHtml(blockData) {
|
|
73
86
|
const { style } = blockData.data;
|
|
74
|
-
const styles = buildStyles(style);
|
|
87
|
+
const styles = buildStyles(style, { perChanges: [], pxChanges: allPxAttributes });
|
|
75
88
|
return appendOutlookSupport(``, styles);
|
|
76
89
|
}
|
|
77
90
|
function convertTextBlock(blockData) {
|
|
78
91
|
const { style, props } = blockData.data;
|
|
79
|
-
const styles = buildStyles(style);
|
|
92
|
+
const styles = buildStyles(style, { perChanges: [], pxChanges: allPxAttributes });
|
|
80
93
|
const text = props.text || "";
|
|
81
94
|
const navigateToUrl = props.navigateToUrl || "";
|
|
82
95
|
const textContent = appendOutlookSupport(text.replaceAll(/\n/g, "<br>"), styles);
|
|
83
96
|
return navigateToUrl ? `<a href="${navigateToUrl}" style="color:inherit; text-decoration:none; cursor:pointer;">${textContent}</a>` : textContent;
|
|
84
97
|
}
|
|
85
|
-
function appendOutlookForImage(content,
|
|
98
|
+
async function appendOutlookForImage(content, outerContainerWidth, innerContainerWidth, imageUrl) {
|
|
99
|
+
const image = await jimp_1.Jimp.read(imageUrl);
|
|
100
|
+
const originalWidth = image.bitmap.width;
|
|
101
|
+
const originalHeight = image.bitmap.height;
|
|
102
|
+
// Calculate width scaling factor based on outer and inner widths
|
|
103
|
+
const widthScalingFactor = Math.min(outerContainerWidth / originalWidth, innerContainerWidth / originalWidth, 1);
|
|
104
|
+
// Scale the height proportionally
|
|
105
|
+
const scaledWidth = Math.round(originalWidth * widthScalingFactor);
|
|
106
|
+
const scaledHeight = Math.round(originalHeight * widthScalingFactor); // Maintain aspect ratio
|
|
107
|
+
// VML for Outlook
|
|
108
|
+
const outlookImage = `<!--[if mso]>
|
|
109
|
+
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="t" stroke="f" style="width:${scaledWidth}px;height:${scaledHeight}px;">
|
|
110
|
+
<v:fill src="${imageUrl}" type="frame" />
|
|
111
|
+
</v:rect>
|
|
112
|
+
<![endif]-->`;
|
|
86
113
|
return `
|
|
87
|
-
|
|
88
|
-
<v:rect xmlns:v="urn:schemas-microsoft-com:vml"
|
|
89
|
-
fill="t" stroke="f"
|
|
90
|
-
style="${imageStyle}">
|
|
91
|
-
<v:fill src="${imageUrl}" type="frame"/>
|
|
92
|
-
</v:rect>
|
|
93
|
-
<![endif]-->
|
|
114
|
+
${outlookImage}
|
|
94
115
|
<!--[if !mso]><!-->
|
|
95
116
|
${content}
|
|
96
117
|
<!--<![endif]-->
|
|
97
118
|
`;
|
|
98
119
|
}
|
|
99
|
-
function convertImageBlock(blockData) {
|
|
120
|
+
async function convertImageBlock(blockData, cellWidthInPx) {
|
|
100
121
|
const { style, props } = blockData.data;
|
|
101
122
|
const { altText, imageUrl, navigateToUrl } = props;
|
|
102
123
|
const { width, height, objectFit, ...containerStyle } = style;
|
|
103
124
|
const imageStyle = { width, height, objectFit };
|
|
104
|
-
const containerStyles = buildStyles(containerStyle);
|
|
105
|
-
const imageTagStyles = buildStyles(imageStyle);
|
|
125
|
+
const containerStyles = buildStyles(containerStyle, { perChanges: [], pxChanges: addPxToAttributes });
|
|
126
|
+
const imageTagStyles = buildStyles(imageStyle, { perChanges: addPxOrPerToAttributes, pxChanges: addPxToAttributes });
|
|
106
127
|
const imageElement = `<img src="${imageUrl}" alt="${altText}" style="${imageTagStyles}" />`;
|
|
107
|
-
const
|
|
128
|
+
const outlookImage = await appendOutlookForImage(imageElement, cellWidthInPx, parseInt(width.replace("%", "")) * cellWidthInPx, imageUrl);
|
|
129
|
+
const imageContent = appendOutlookSupport(outlookImage, containerStyles);
|
|
108
130
|
return navigateToUrl ? `<a href="${navigateToUrl}" target="_blank" style="display:block; text-decoration:none; cursor:pointer;">${imageContent}</a>` : imageContent;
|
|
109
131
|
}
|
|
110
132
|
function appendOutlookForButton(content, buttonStyle, navigateToUrl, text) {
|
|
@@ -139,26 +161,32 @@ function convertButtonBlock(blockData) {
|
|
|
139
161
|
textColor,
|
|
140
162
|
backgroundColor: buttonColor,
|
|
141
163
|
};
|
|
142
|
-
const convertedButtonStyle = buildStyles(buttonStyle);
|
|
143
|
-
const convertedStyles = buildStyles(rest);
|
|
164
|
+
const convertedButtonStyle = buildStyles(buttonStyle, { perChanges: [], pxChanges: allPxAttributes });
|
|
165
|
+
const convertedStyles = buildStyles(rest, { perChanges: [], pxChanges: allPxAttributes });
|
|
144
166
|
const buttonElement = `<a href="${navigateToUrl}" style="display:inline-block; text-decoration:none; cursor:pointer;"><button style="${convertedButtonStyle}">${text}</button></a>`;
|
|
145
167
|
const buttonContent = appendOutlookSupport(appendOutlookForButton(buttonElement, style, navigateToUrl, text), convertedStyles);
|
|
146
168
|
return buttonContent;
|
|
147
169
|
}
|
|
148
|
-
function
|
|
149
|
-
const
|
|
150
|
-
const { columnGap, ...rest } = style;
|
|
151
|
-
const { rows, columns, cellWidths } = props;
|
|
152
|
-
const styles = buildStyles(rest);
|
|
153
|
-
const gridItems = [];
|
|
170
|
+
async function processGridItemsInParallel(columns, childrenIds, cellWidths, cellWidthInPx, rootData) {
|
|
171
|
+
const gridItemPromises = [];
|
|
154
172
|
for (let colIndex = 0; colIndex < columns; colIndex++) {
|
|
155
173
|
const childId = childrenIds[colIndex];
|
|
156
174
|
const cellWidth = cellWidths ? cellWidths[colIndex] : 100 / columns;
|
|
157
175
|
const childBlockData = rootData[childId];
|
|
158
176
|
if (childBlockData) {
|
|
159
|
-
|
|
177
|
+
const gridItemPromise = convertGridCellBlock(childBlockData, rootData, cellWidth, cellWidthInPx);
|
|
178
|
+
gridItemPromises.push(gridItemPromise);
|
|
160
179
|
}
|
|
161
180
|
}
|
|
181
|
+
const gridItems = await Promise.all(gridItemPromises);
|
|
182
|
+
return gridItems;
|
|
183
|
+
}
|
|
184
|
+
async function convertGridBlock(blockData, rootData, cellWidthInPx) {
|
|
185
|
+
const { style, childrenIds = [], props } = blockData.data;
|
|
186
|
+
const { columnGap, ...rest } = style;
|
|
187
|
+
const { rows, columns, cellWidths } = props;
|
|
188
|
+
const styles = buildStyles(rest, { perChanges: [], pxChanges: allPxAttributes });
|
|
189
|
+
const gridItems = await processGridItemsInParallel(columns, childrenIds, cellWidths, cellWidthInPx, rootData);
|
|
162
190
|
return `
|
|
163
191
|
<table cellspacing="${columnGap}" style="width:100%; max-width:100%; ${styles}">
|
|
164
192
|
<tbody>
|
|
@@ -167,9 +195,14 @@ function convertGridBlock(blockData, rootData) {
|
|
|
167
195
|
</table>
|
|
168
196
|
`;
|
|
169
197
|
}
|
|
170
|
-
function convertGridCellBlock(blockData, rootData, cellWidth) {
|
|
198
|
+
async function convertGridCellBlock(blockData, rootData, cellWidth, parentCellWidth) {
|
|
171
199
|
const { style, childrenIds } = blockData.data;
|
|
172
|
-
const styles = buildStyles(style);
|
|
173
|
-
const cellItems =
|
|
174
|
-
|
|
200
|
+
const styles = buildStyles(style, { perChanges: [], pxChanges: allPxAttributes });
|
|
201
|
+
const cellItems = [];
|
|
202
|
+
if (childrenIds && childrenIds?.length > 0) {
|
|
203
|
+
for (const childId of childrenIds) {
|
|
204
|
+
cellItems.push(await convertToHtml(rootData[childId], rootData, parentCellWidth * ((style.width || "0").replace("px", "") / 100)));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return `<td style="width:${cellWidth}% ; max-width:${cellWidth}%; ${styles}">${cellItems.join("")}</td>`;
|
|
175
208
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "email-builder-utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
"description": "",
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/node": "^22.13.10",
|
|
27
|
+
"@types/pngjs": "^6.0.5",
|
|
27
28
|
"typescript": "^5.8.2"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"jimp": "^1.6.0"
|
|
28
32
|
}
|
|
29
33
|
}
|