email-builder-utils 1.1.1 → 1.1.3
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.
- package/dist/utils/convertJsonToHtml.js +42 -42
- package/dist/utils/jsonToHTML.d.ts.map +1 -1
- package/dist/utils/jsonToHTML.js +58 -48
- package/package.json +33 -33
|
@@ -11,48 +11,48 @@ const convertJsonToHtml = async (jsonData) => {
|
|
|
11
11
|
(rootData.style?.padding?.left || 0) -
|
|
12
12
|
(rootData.style?.padding?.right || 0)));
|
|
13
13
|
}
|
|
14
|
-
const rawHtml = `
|
|
15
|
-
<!DOCTYPE html>
|
|
16
|
-
<html lang="en">
|
|
17
|
-
<head>
|
|
18
|
-
<meta charset="UTF-8" />
|
|
19
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
20
|
-
<title>Email Layout</title>
|
|
21
|
-
<style>
|
|
22
|
-
.responsive-table {
|
|
23
|
-
width: 100%;
|
|
24
|
-
max-width: 600px;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
@media only screen and (max-width: 600px) {
|
|
28
|
-
.stack-column {
|
|
29
|
-
display: block !important;
|
|
30
|
-
width: 100% !important;
|
|
31
|
-
max-width: 100% !important;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
</style>
|
|
35
|
-
</head>
|
|
36
|
-
<body>
|
|
37
|
-
<center>
|
|
38
|
-
<table
|
|
39
|
-
class="responsive-table"
|
|
40
|
-
style="font-family: ${rootData.style?.fontFamily};
|
|
41
|
-
margin: 0 auto;
|
|
42
|
-
background-color: ${rootData.style?.canvasColor};
|
|
43
|
-
color: ${rootData.style?.textColor};
|
|
44
|
-
${jsonToHTML_1.tableCommonStyle}"
|
|
45
|
-
>
|
|
46
|
-
<tbody>
|
|
47
|
-
<tr>
|
|
48
|
-
<td style="padding: 0;">
|
|
49
|
-
${blocksHtml.join("")}
|
|
50
|
-
</td>
|
|
51
|
-
</tr>
|
|
52
|
-
</tbody>
|
|
53
|
-
</table>
|
|
54
|
-
</center>
|
|
55
|
-
</body>
|
|
14
|
+
const rawHtml = `
|
|
15
|
+
<!DOCTYPE html>
|
|
16
|
+
<html lang="en">
|
|
17
|
+
<head>
|
|
18
|
+
<meta charset="UTF-8" />
|
|
19
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
20
|
+
<title>Email Layout</title>
|
|
21
|
+
<style>
|
|
22
|
+
.responsive-table {
|
|
23
|
+
width: 100%;
|
|
24
|
+
max-width: 600px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@media only screen and (max-width: 600px) {
|
|
28
|
+
.stack-column {
|
|
29
|
+
display: block !important;
|
|
30
|
+
width: 100% !important;
|
|
31
|
+
max-width: 100% !important;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
35
|
+
</head>
|
|
36
|
+
<body>
|
|
37
|
+
<center>
|
|
38
|
+
<table
|
|
39
|
+
class="responsive-table"
|
|
40
|
+
style="font-family: ${rootData.style?.fontFamily};
|
|
41
|
+
margin: 0 auto;
|
|
42
|
+
background-color: ${rootData.style?.canvasColor};
|
|
43
|
+
color: ${rootData.style?.textColor};
|
|
44
|
+
${jsonToHTML_1.tableCommonStyle}"
|
|
45
|
+
>
|
|
46
|
+
<tbody>
|
|
47
|
+
<tr>
|
|
48
|
+
<td style="padding: 0;">
|
|
49
|
+
${blocksHtml.join("")}
|
|
50
|
+
</td>
|
|
51
|
+
</tr>
|
|
52
|
+
</tbody>
|
|
53
|
+
</table>
|
|
54
|
+
</center>
|
|
55
|
+
</body>
|
|
56
56
|
</html>`;
|
|
57
57
|
return rawHtml;
|
|
58
58
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonToHTML.d.ts","sourceRoot":"","sources":["../../src/utils/jsonToHTML.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"jsonToHTML.d.ts","sourceRoot":"","sources":["../../src/utils/jsonToHTML.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAUrC,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;AAMD,eAAO,MAAM,gBAAgB,kDAAkD,CAAC;AAiDhF,wBAAsB,aAAa,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAiB9F"}
|
package/dist/utils/jsonToHTML.js
CHANGED
|
@@ -11,9 +11,8 @@ exports.tableCommonStyle = "border-collapse:collapse; table-layout:fixed;";
|
|
|
11
11
|
function cleanJson(obj) {
|
|
12
12
|
if (typeof obj !== "object" || obj === null)
|
|
13
13
|
return obj;
|
|
14
|
-
if (Array.isArray(obj))
|
|
14
|
+
if (Array.isArray(obj))
|
|
15
15
|
return obj.map(cleanJson);
|
|
16
|
-
}
|
|
17
16
|
return Object.fromEntries(Object.entries(obj)
|
|
18
17
|
.filter(([_, value]) => value !== undefined && value !== null && value !== "")
|
|
19
18
|
.map(([key, value]) => [key, cleanJson(value)]));
|
|
@@ -21,41 +20,35 @@ function cleanJson(obj) {
|
|
|
21
20
|
function jsonToPlainString(obj) {
|
|
22
21
|
if (typeof obj !== "object" || obj === null)
|
|
23
22
|
return String(obj);
|
|
24
|
-
if (Array.isArray(obj))
|
|
23
|
+
if (Array.isArray(obj))
|
|
25
24
|
return obj.map(jsonToPlainString).join(", ");
|
|
26
|
-
}
|
|
27
25
|
return Object.entries(obj)
|
|
28
26
|
.map(([key, value]) => `${key}:${jsonToPlainString(value)}; `)
|
|
29
27
|
.join("");
|
|
30
28
|
}
|
|
31
29
|
function buildStyles(style, { pxChanges, perChanges }) {
|
|
32
|
-
if (!style)
|
|
30
|
+
if (!style)
|
|
33
31
|
style = {};
|
|
34
|
-
}
|
|
35
32
|
const stylesObj = {};
|
|
36
33
|
Object.entries(style).forEach(([key, value]) => {
|
|
37
|
-
if (key === "customCss")
|
|
34
|
+
if (key === "customCss")
|
|
38
35
|
return;
|
|
39
|
-
}
|
|
40
|
-
const appendPx = pxChanges.includes(key);
|
|
41
|
-
const appendPer = perChanges.includes(key);
|
|
42
36
|
if (value === undefined || value === null || value === "")
|
|
43
|
-
return
|
|
44
|
-
if ((key === "padding" || key === "buttonPadding") && typeof value === "object"
|
|
37
|
+
return;
|
|
38
|
+
if ((key === "padding" || key === "buttonPadding") && typeof value === "object") {
|
|
45
39
|
const padding = value;
|
|
46
40
|
value = `${padding.top}px ${padding.right}px ${padding.bottom}px ${padding.left}px`;
|
|
47
41
|
}
|
|
48
42
|
const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
49
|
-
if (
|
|
43
|
+
if (pxChanges.includes(key)) {
|
|
50
44
|
stylesObj[cssKey] = `${value}px`;
|
|
51
45
|
}
|
|
52
|
-
else if (
|
|
46
|
+
else if (perChanges.includes(key)) {
|
|
53
47
|
stylesObj[cssKey] = `${value}%`;
|
|
54
48
|
}
|
|
55
49
|
else {
|
|
56
50
|
stylesObj[cssKey] = value;
|
|
57
51
|
}
|
|
58
|
-
return;
|
|
59
52
|
});
|
|
60
53
|
return `${jsonToPlainString(cleanJson(stylesObj))}${style.customCss || ""}`.trim();
|
|
61
54
|
}
|
|
@@ -78,8 +71,8 @@ async function convertToHtml(blockData, rootData, cellWidthInPx) {
|
|
|
78
71
|
}
|
|
79
72
|
}
|
|
80
73
|
function appendOutlookSupport(content, contentStyle) {
|
|
81
|
-
return `
|
|
82
|
-
<table width="100%" style="${exports.tableCommonStyle}"><tr><td style="${contentStyle}">${content}</td></tr></table>
|
|
74
|
+
return `
|
|
75
|
+
<table width="100%" style="${exports.tableCommonStyle}"><tr><td style="${contentStyle}">${content}</td></tr></table>
|
|
83
76
|
`;
|
|
84
77
|
}
|
|
85
78
|
function convertDividerBlockToHtml(blockData) {
|
|
@@ -101,26 +94,41 @@ function convertTextBlock(blockData) {
|
|
|
101
94
|
const textContent = appendOutlookSupport(text.replaceAll(/\n/g, "<br>"), styles);
|
|
102
95
|
return navigateToUrl ? `<a href="${navigateToUrl}" style="color:inherit; text-decoration:none; cursor:pointer;">${textContent}</a>` : textContent;
|
|
103
96
|
}
|
|
104
|
-
async function appendOutlookForImage(content, outerContainerWidth, innerContainerWidth, imageUrl) {
|
|
97
|
+
async function appendOutlookForImage(content, outerContainerWidth, innerContainerWidth, imageUrl, style = {}) {
|
|
105
98
|
const image = await jimp_1.Jimp.read(imageUrl);
|
|
106
99
|
const originalWidth = image.bitmap.width;
|
|
107
100
|
const originalHeight = image.bitmap.height;
|
|
108
101
|
// Calculate width scaling factor based on outer and inner widths
|
|
109
102
|
const widthScalingFactor = Math.min(outerContainerWidth / originalWidth, innerContainerWidth / originalWidth);
|
|
110
|
-
// Scale
|
|
103
|
+
// Scale height proportionally
|
|
111
104
|
const scaledWidth = Math.round(originalWidth * widthScalingFactor);
|
|
112
105
|
const scaledHeight = Math.round(originalHeight * widthScalingFactor); // Maintain aspect ratio
|
|
113
|
-
//
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
106
|
+
// Extract border styles
|
|
107
|
+
const borderWidth = style?.borderWidth || 0;
|
|
108
|
+
const borderColor = style?.borderColor || "transparent";
|
|
109
|
+
const borderRadius = style?.borderRadius || 0;
|
|
110
|
+
const useRoundRect = borderRadius > 0;
|
|
111
|
+
const arcsize = useRoundRect ? Math.min(borderRadius / scaledWidth, 1) : 0;
|
|
112
|
+
// Conditionally add stroke attributes
|
|
113
|
+
const borderAttributes = borderWidth > 0
|
|
114
|
+
? `strokeweight="${borderWidth}px" strokecolor="${borderColor}"`
|
|
115
|
+
: `stroked="false"`;
|
|
116
|
+
const outlookImage = `<!--[if mso]>
|
|
117
|
+
<v:${useRoundRect ? "roundrect" : "rect"} xmlns:v="urn:schemas-microsoft-com:vml"
|
|
118
|
+
style="width:${scaledWidth}px;height:${scaledHeight}px;"
|
|
119
|
+
${borderAttributes}
|
|
120
|
+
${useRoundRect ? `arcsize="${arcsize}"` : ""}
|
|
121
|
+
fill="true"
|
|
122
|
+
fillcolor="none">
|
|
123
|
+
<v:fill src="${imageUrl}" type="frame" />
|
|
124
|
+
<v:textbox inset="0,0,0,0"><div style="display:none;">.</div></v:textbox>
|
|
125
|
+
</v:${useRoundRect ? "roundrect" : "rect"}>
|
|
118
126
|
<![endif]-->`;
|
|
119
|
-
return `
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
return `
|
|
128
|
+
${outlookImage}
|
|
129
|
+
<!--[if !mso]><!-->
|
|
130
|
+
${content}
|
|
131
|
+
<!--<![endif]-->
|
|
124
132
|
`;
|
|
125
133
|
}
|
|
126
134
|
async function convertImageBlock(blockData, cellWidthInPx) {
|
|
@@ -131,24 +139,26 @@ async function convertImageBlock(blockData, cellWidthInPx) {
|
|
|
131
139
|
const containerStyles = buildStyles(containerStyle, { perChanges: [], pxChanges: addPxToAttributes });
|
|
132
140
|
const imageTagStyles = buildStyles(imageStyle, { perChanges: addPxOrPerToAttributes, pxChanges: addPxToAttributes });
|
|
133
141
|
const imageElement = `<img src="${imageUrl}" alt="${altText}" style="${imageTagStyles}" />`;
|
|
134
|
-
const outlookImage = await appendOutlookForImage(imageElement, cellWidthInPx, ((typeof width === "string" ? parseInt(width.replace("%", "")) : width) / 100) *
|
|
142
|
+
const outlookImage = await appendOutlookForImage(imageElement, cellWidthInPx, ((typeof width === "string" ? parseInt(width.replace("%", "")) : width) / 100) *
|
|
143
|
+
(cellWidthInPx - style?.padding?.left - style?.padding?.right), imageUrl, style);
|
|
135
144
|
const imageContent = appendOutlookSupport(outlookImage, containerStyles);
|
|
145
|
+
console.log(imageContent);
|
|
136
146
|
return navigateToUrl ? `<a href="${navigateToUrl}" target="_blank" style="display:block; text-decoration:none; cursor:pointer;">${imageContent}</a>` : imageContent;
|
|
137
147
|
}
|
|
138
148
|
function appendOutlookForButton(content, buttonStyle, navigateToUrl, text) {
|
|
139
149
|
const { width, buttonColor, borderColor, borderRadius, borderWidth, height, buttonPadding, color, fontFamily, fontSize, fontWeight } = buttonStyle;
|
|
140
|
-
return `
|
|
141
|
-
<!--[if mso]>
|
|
142
|
-
<v:${borderRadius ? "roundrect" : "rect"} xmlns:v="urn:schemas-microsoft-com:vml" href="${navigateToUrl}" xmlns:w="urn:schemas-microsoft-com:office:word" style="height:${height || 44}px;width:${width || 200}px;v-text-anchor:middle;" arcsize="${borderRadius || 0}px" strokeweight="${borderWidth || 0}px" strokecolor="${borderColor || "transparent"}" fillcolor="${buttonColor || "none"}">
|
|
143
|
-
<w:anchorlock/>
|
|
144
|
-
<v:textbox inset="${buttonPadding?.top || 0}px, ${buttonPadding?.left || 0}px, ${buttonPadding?.bottom || 0}px, ${buttonPadding?.right || 0}px">
|
|
145
|
-
<center style="font-family:${fontFamily || ""};font-size:${fontSize}px;font-weight:${fontWeight};color:${color};">${text}</center>
|
|
146
|
-
</v:textbox>
|
|
147
|
-
</v:${borderRadius ? "roundrect" : "rect"}>
|
|
148
|
-
<![endif]-->
|
|
149
|
-
<!--[if !mso]><!-->
|
|
150
|
-
${content}
|
|
151
|
-
<!--<![endif]-->
|
|
150
|
+
return `
|
|
151
|
+
<!--[if mso]>
|
|
152
|
+
<v:${borderRadius ? "roundrect" : "rect"} xmlns:v="urn:schemas-microsoft-com:vml" href="${navigateToUrl}" xmlns:w="urn:schemas-microsoft-com:office:word" style="height:${height || 44}px;width:${width || 200}px;v-text-anchor:middle;" arcsize="${borderRadius || 0}px" strokeweight="${borderWidth || 0}px" strokecolor="${borderColor || "transparent"}" fillcolor="${buttonColor || "none"}">
|
|
153
|
+
<w:anchorlock/>
|
|
154
|
+
<v:textbox inset="${buttonPadding?.top || 0}px, ${buttonPadding?.left || 0}px, ${buttonPadding?.bottom || 0}px, ${buttonPadding?.right || 0}px">
|
|
155
|
+
<center style="font-family:${fontFamily || ""};font-size:${fontSize}px;font-weight:${fontWeight};color:${color};">${text}</center>
|
|
156
|
+
</v:textbox>
|
|
157
|
+
</v:${borderRadius ? "roundrect" : "rect"}>
|
|
158
|
+
<![endif]-->
|
|
159
|
+
<!--[if !mso]><!-->
|
|
160
|
+
${content}
|
|
161
|
+
<!--<![endif]-->
|
|
152
162
|
`;
|
|
153
163
|
}
|
|
154
164
|
function convertButtonBlock(blockData) {
|
|
@@ -195,12 +205,12 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
|
|
|
195
205
|
const { rows, columns, cellWidths } = props;
|
|
196
206
|
const styles = buildStyles(rest, { perChanges: [], pxChanges: allPxAttributes });
|
|
197
207
|
const gridItems = await processGridItemsInParallel(columns, childrenIds, cellWidths, cellWidthInPx, rootData);
|
|
198
|
-
return `
|
|
199
|
-
<table cellspacing="${columnGap}" style="width:100%; max-width:100%; ${styles}">
|
|
200
|
-
<tbody>
|
|
201
|
-
<tr>${gridItems.join("")}</tr>
|
|
202
|
-
</tbody>
|
|
203
|
-
</table>
|
|
208
|
+
return `
|
|
209
|
+
<table cellspacing="${columnGap}" style="width:100%; max-width:100%; ${styles}">
|
|
210
|
+
<tbody>
|
|
211
|
+
<tr>${gridItems.join("")}</tr>
|
|
212
|
+
</tbody>
|
|
213
|
+
</table>
|
|
204
214
|
`;
|
|
205
215
|
}
|
|
206
216
|
async function convertGridCellBlock(blockData, rootData, cellWidth, parentCellWidth) {
|
package/package.json
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "email-builder-utils",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"main": "dist/index.js",
|
|
5
|
-
"types": "dist/index.d.ts",
|
|
6
|
-
"files": [
|
|
7
|
-
"dist"
|
|
8
|
-
],
|
|
9
|
-
"scripts": {
|
|
10
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
-
"build": "tsc",
|
|
12
|
-
"start": "npm run build && node dist/index.js"
|
|
13
|
-
},
|
|
14
|
-
"repository": {
|
|
15
|
-
"type": "git",
|
|
16
|
-
"url": "git+https://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@github.com/Biztecno-Infra/email-builder-utils.git"
|
|
17
|
-
},
|
|
18
|
-
"author": "",
|
|
19
|
-
"license": "ISC",
|
|
20
|
-
"bugs": {
|
|
21
|
-
"url": "https://github.com/Biztecno-Infra/email-builder-utils/issues"
|
|
22
|
-
},
|
|
23
|
-
"homepage": "https://github.com/Biztecno-Infra/email-builder-utils#readme",
|
|
24
|
-
"description": "",
|
|
25
|
-
"devDependencies": {
|
|
26
|
-
"@types/node": "^22.13.10",
|
|
27
|
-
"@types/pngjs": "^6.0.5",
|
|
28
|
-
"typescript": "^5.8.2"
|
|
29
|
-
},
|
|
30
|
-
"dependencies": {
|
|
31
|
-
"jimp": "^1.6.0"
|
|
32
|
-
}
|
|
33
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "email-builder-utils",
|
|
3
|
+
"version": "1.1.3",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "npm run build && node dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://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@github.com/Biztecno-Infra/email-builder-utils.git"
|
|
17
|
+
},
|
|
18
|
+
"author": "",
|
|
19
|
+
"license": "ISC",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/Biztecno-Infra/email-builder-utils/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/Biztecno-Infra/email-builder-utils#readme",
|
|
24
|
+
"description": "",
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.13.10",
|
|
27
|
+
"@types/pngjs": "^6.0.5",
|
|
28
|
+
"typescript": "^5.8.2"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"jimp": "^1.6.0"
|
|
32
|
+
}
|
|
33
|
+
}
|