email-builder-utils 1.1.35 → 1.1.40
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 +79 -79
- package/dist/utils/jsonToHTML.d.ts.map +1 -1
- package/dist/utils/jsonToHTML.js +398 -305
- package/package.json +33 -33
|
@@ -12,85 +12,85 @@ const convertJsonToHtml = async (jsonData) => {
|
|
|
12
12
|
}
|
|
13
13
|
const { fontFamily, canvasColor, textColor, padding = {}, borderColor, borderRadius, borderWidth, borderStyle, } = rootData.style || {};
|
|
14
14
|
const { top = 0, right = 0, bottom = 0, left = 0 } = padding;
|
|
15
|
-
const rawHtml = `<!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
|
-
<meta name="x-apple-disable-message-reformatting" />
|
|
21
|
-
<style>
|
|
22
|
-
.responsive-table {
|
|
23
|
-
width: 100%;
|
|
24
|
-
max-width: 600px;
|
|
25
|
-
}
|
|
26
|
-
@media only screen and (max-width: 600px) {
|
|
27
|
-
.responsive-table {
|
|
28
|
-
width: 100% !important;
|
|
29
|
-
}
|
|
30
|
-
.stack-column,
|
|
31
|
-
.stack-column td {
|
|
32
|
-
display: block !important;
|
|
33
|
-
width: 100% !important;
|
|
34
|
-
max-width: 100% !important;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
.hide-mobile {
|
|
38
|
-
display: block !important;
|
|
39
|
-
mso-hide: all !important; /* Hide in Outlook */
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.hide-desktop {
|
|
43
|
-
display: block !important;
|
|
44
|
-
mso-hide: all !important; /* Hide in Outlook */
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
@media only screen and (max-width: 600px) {
|
|
48
|
-
.hide-mobile {
|
|
49
|
-
display: none !important;
|
|
50
|
-
max-height: 0 !important;
|
|
51
|
-
overflow: hidden !important;
|
|
52
|
-
mso-hide: all !important;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
@media only screen and (min-width: 601px) {
|
|
57
|
-
.hide-desktop {
|
|
58
|
-
display: none !important;
|
|
59
|
-
max-height: 0 !important;
|
|
60
|
-
overflow: hidden !important;
|
|
61
|
-
mso-hide: all !important;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
</style>
|
|
66
|
-
</head>
|
|
67
|
-
<body>
|
|
68
|
-
<center>
|
|
69
|
-
<table
|
|
70
|
-
class="responsive-table"
|
|
71
|
-
bgcolor="${canvasColor}"
|
|
72
|
-
style="
|
|
73
|
-
font-family: ${fontFamily};
|
|
74
|
-
margin: 0 auto;
|
|
75
|
-
table-layout:fixed;
|
|
76
|
-
width:600px;
|
|
77
|
-
max-width:600px;
|
|
78
|
-
background-color: ${canvasColor};
|
|
79
|
-
color: ${textColor};
|
|
80
|
-
padding: ${top}px ${right}px ${bottom}px ${left}px;
|
|
81
|
-
border: ${borderWidth}px ${borderStyle} ${borderColor};
|
|
82
|
-
border-radius: ${borderRadius}px; "
|
|
83
|
-
>
|
|
84
|
-
<tbody>
|
|
85
|
-
<tr>
|
|
86
|
-
<td style="padding: 0;">
|
|
87
|
-
${blocksHtml.join("")}
|
|
88
|
-
</td>
|
|
89
|
-
</tr>
|
|
90
|
-
</tbody>
|
|
91
|
-
</table>
|
|
92
|
-
</center>
|
|
93
|
-
</body>
|
|
15
|
+
const rawHtml = `<!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
|
+
<meta name="x-apple-disable-message-reformatting" />
|
|
21
|
+
<style>
|
|
22
|
+
.responsive-table {
|
|
23
|
+
width: 100%;
|
|
24
|
+
max-width: 600px;
|
|
25
|
+
}
|
|
26
|
+
@media only screen and (max-width: 600px) {
|
|
27
|
+
.responsive-table {
|
|
28
|
+
width: 100% !important;
|
|
29
|
+
}
|
|
30
|
+
.stack-column,
|
|
31
|
+
.stack-column td {
|
|
32
|
+
display: block !important;
|
|
33
|
+
width: 100% !important;
|
|
34
|
+
max-width: 100% !important;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
.hide-mobile {
|
|
38
|
+
display: block !important;
|
|
39
|
+
mso-hide: all !important; /* Hide in Outlook */
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.hide-desktop {
|
|
43
|
+
display: block !important;
|
|
44
|
+
mso-hide: all !important; /* Hide in Outlook */
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@media only screen and (max-width: 600px) {
|
|
48
|
+
.hide-mobile {
|
|
49
|
+
display: none !important;
|
|
50
|
+
max-height: 0 !important;
|
|
51
|
+
overflow: hidden !important;
|
|
52
|
+
mso-hide: all !important;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@media only screen and (min-width: 601px) {
|
|
57
|
+
.hide-desktop {
|
|
58
|
+
display: none !important;
|
|
59
|
+
max-height: 0 !important;
|
|
60
|
+
overflow: hidden !important;
|
|
61
|
+
mso-hide: all !important;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
</style>
|
|
66
|
+
</head>
|
|
67
|
+
<body>
|
|
68
|
+
<center>
|
|
69
|
+
<table
|
|
70
|
+
class="responsive-table"
|
|
71
|
+
bgcolor="${canvasColor}"
|
|
72
|
+
style="
|
|
73
|
+
font-family: ${fontFamily};
|
|
74
|
+
margin: 0 auto;
|
|
75
|
+
table-layout:fixed;
|
|
76
|
+
width:600px;
|
|
77
|
+
max-width:600px;
|
|
78
|
+
background-color: ${canvasColor};
|
|
79
|
+
color: ${textColor};
|
|
80
|
+
padding: ${top}px ${right}px ${bottom}px ${left}px;
|
|
81
|
+
border: ${borderWidth}px ${borderStyle} ${borderColor};
|
|
82
|
+
border-radius: ${borderRadius}px; "
|
|
83
|
+
>
|
|
84
|
+
<tbody>
|
|
85
|
+
<tr>
|
|
86
|
+
<td style="padding: 0;">
|
|
87
|
+
${blocksHtml.join("")}
|
|
88
|
+
</td>
|
|
89
|
+
</tr>
|
|
90
|
+
</tbody>
|
|
91
|
+
</table>
|
|
92
|
+
</center>
|
|
93
|
+
</body>
|
|
94
94
|
</html>`;
|
|
95
95
|
return rawHtml;
|
|
96
96
|
};
|
|
@@ -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;AAGrC,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;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;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;AAYD,eAAO,MAAM,gBAAgB,kDAAkD,CAAC;
|
|
1
|
+
{"version":3,"file":"jsonToHTML.d.ts","sourceRoot":"","sources":["../../src/utils/jsonToHTML.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAGrC,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;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;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;AAYD,eAAO,MAAM,gBAAgB,kDAAkD,CAAC;AAwFhF,wBAAsB,aAAa,CACjC,SAAS,EAAE,UAAU,EACrB,QAAQ,EAAE,GAAG,EACb,aAAa,EAAE,MAAM,mBAwBtB;AA4qBD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAyL5E"}
|
package/dist/utils/jsonToHTML.js
CHANGED
|
@@ -127,14 +127,17 @@ function appendOutlookSupport(content, contentStyle, className) {
|
|
|
127
127
|
const visibilityClass = className || "";
|
|
128
128
|
const shouldHideInOutlook = visibilityClass.includes("hide-desktop");
|
|
129
129
|
if (shouldHideInOutlook) {
|
|
130
|
-
return `
|
|
131
|
-
<!--[if !mso]><!-->
|
|
132
|
-
<table width="100%" style="${exports.tableCommonStyle}" class="${visibilityClass}"><tr><td style="${contentStyle}">${content}</td></tr></table>
|
|
133
|
-
<!--<![endif]-->
|
|
130
|
+
return `
|
|
131
|
+
<!--[if !mso]><!-->
|
|
132
|
+
<table width="100%" style="${exports.tableCommonStyle}" class="${visibilityClass}"><tr><td style="${contentStyle}">${content}</td></tr></table>
|
|
133
|
+
<!--<![endif]-->
|
|
134
134
|
`;
|
|
135
135
|
}
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
// Use a single table that works for both MSO and non-MSO
|
|
137
|
+
// The content inside already has its own MSO conditional comments
|
|
138
|
+
// width="100%" works for modern clients, Outlook will use the td width from nested tables
|
|
139
|
+
return `
|
|
140
|
+
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="${exports.tableCommonStyle}; max-width:600px;" class="${visibilityClass}"><tr><td width="100%" style="${contentStyle}">${content}</td></tr></table>
|
|
138
141
|
`;
|
|
139
142
|
}
|
|
140
143
|
function convertDividerBlockToHtml(blockData) {
|
|
@@ -153,21 +156,21 @@ function convertDividerBlockToHtml(blockData) {
|
|
|
153
156
|
]
|
|
154
157
|
.filter(Boolean)
|
|
155
158
|
.join(" ");
|
|
156
|
-
const dividerContent = `
|
|
157
|
-
<table
|
|
158
|
-
width="${dividerWidth}%"
|
|
159
|
-
cellpadding="0"
|
|
160
|
-
cellspacing="0"
|
|
161
|
-
>
|
|
162
|
-
<tr>
|
|
163
|
-
<td
|
|
164
|
-
height="${thickness}"
|
|
165
|
-
style="font-size:1px; line-height:1px; background:${dividerColor}; width:${dividerWidth};"
|
|
166
|
-
>
|
|
167
|
-
|
|
168
|
-
</td>
|
|
169
|
-
</tr>
|
|
170
|
-
</table>
|
|
159
|
+
const dividerContent = `
|
|
160
|
+
<table
|
|
161
|
+
width="${dividerWidth}%"
|
|
162
|
+
cellpadding="0"
|
|
163
|
+
cellspacing="0"
|
|
164
|
+
>
|
|
165
|
+
<tr>
|
|
166
|
+
<td
|
|
167
|
+
height="${thickness}"
|
|
168
|
+
style="font-size:1px; line-height:1px; background:${dividerColor}; width:${dividerWidth};"
|
|
169
|
+
>
|
|
170
|
+
|
|
171
|
+
</td>
|
|
172
|
+
</tr>
|
|
173
|
+
</table>
|
|
171
174
|
`;
|
|
172
175
|
return appendOutlookSupport(dividerContent, convertedStyle, visibilityClass);
|
|
173
176
|
}
|
|
@@ -231,38 +234,79 @@ function convertTextBlock(blockData) {
|
|
|
231
234
|
? `<a href="${navigateToUrl}" rel="noreferrer noopener" style="color:inherit;text-decoration:none;cursor:pointer;">${textContent}</a>`
|
|
232
235
|
: textContent;
|
|
233
236
|
}
|
|
234
|
-
async function appendOutlookForImage(content, outerContainerWidth, innerContainerWidth, imageUrl, style = {}) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
async function appendOutlookForImage(content, outerContainerWidth, innerContainerWidth, imageUrl, style = {}, finalWidth, finalHeight) {
|
|
238
|
+
// OUTLOOK FIX: Use provided dimensions or calculate from image
|
|
239
|
+
let vmlWidth;
|
|
240
|
+
let vmlHeight;
|
|
241
|
+
if (finalWidth && finalHeight) {
|
|
242
|
+
// Use pre-calculated dimensions (preferred for accuracy)
|
|
243
|
+
vmlWidth = finalWidth;
|
|
244
|
+
vmlHeight = finalHeight;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// Fallback: calculate from image
|
|
248
|
+
const image = await jimp_1.Jimp.read(imageUrl);
|
|
249
|
+
const originalWidth = image.bitmap.width;
|
|
250
|
+
const originalHeight = image.bitmap.height;
|
|
251
|
+
const widthScalingFactor = Math.min(outerContainerWidth / originalWidth, innerContainerWidth / originalWidth, 1 // Never scale up
|
|
252
|
+
);
|
|
253
|
+
vmlWidth = Math.round(originalWidth * widthScalingFactor);
|
|
254
|
+
vmlHeight = Math.round(originalHeight * widthScalingFactor);
|
|
255
|
+
}
|
|
241
256
|
const borderWidth = parseInt(style?.borderWidth) || 0;
|
|
242
257
|
const borderColor = style?.borderColor || "transparent";
|
|
243
258
|
const borderRadius = parseInt(style?.borderRadius) || 0;
|
|
244
259
|
const useRoundRect = borderRadius > 0;
|
|
245
260
|
const arcsize = useRoundRect
|
|
246
|
-
? Math.min(borderRadius /
|
|
261
|
+
? Math.min(borderRadius / vmlHeight, 1).toFixed(2)
|
|
247
262
|
: "";
|
|
248
263
|
const borderAttributes = borderWidth > 0
|
|
249
264
|
? `strokeweight="${borderWidth}px" strokecolor="${borderColor}"`
|
|
250
265
|
: `stroked="false"`;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
266
|
+
// OUTLOOK FIX: For Outlook 2019+ (version 2512), VML type="frame" causes stretching
|
|
267
|
+
// Solution: Use simple IMG tag with fixed dimensions for Outlook, only use VML for border radius
|
|
268
|
+
let outlookImage;
|
|
269
|
+
if (useRoundRect && borderRadius > 0) {
|
|
270
|
+
// Use VML for border radius - wrap in table to constrain width for Old Outlook (Word engine)
|
|
271
|
+
// Use aspect="atmost" to prevent image from stretching beyond its bounds
|
|
272
|
+
outlookImage = `<!--[if mso]>
|
|
273
|
+
<table border="0" cellpadding="0" cellspacing="0" width="${vmlWidth}" style="width:${vmlWidth}px;">
|
|
274
|
+
<tr>
|
|
275
|
+
<td align="center" valign="top" width="${vmlWidth}" style="width:${vmlWidth}px;">
|
|
276
|
+
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml"
|
|
277
|
+
style="width:${vmlWidth}px;height:${vmlHeight}px;"
|
|
278
|
+
${borderAttributes}
|
|
279
|
+
arcsize="${arcsize}"
|
|
280
|
+
fill="true" fillcolor="none">
|
|
281
|
+
<v:fill src="${imageUrl}" type="tile" aspect="atmost" />
|
|
282
|
+
<v:textbox inset="0,0,0,0"><div style="display:none;">.</div></v:textbox>
|
|
283
|
+
</v:roundrect>
|
|
284
|
+
</td>
|
|
285
|
+
</tr>
|
|
286
|
+
</table>
|
|
287
|
+
<![endif]-->`;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
// For images without border radius, wrap in a table with explicit width for Old Outlook (Word engine)
|
|
291
|
+
// This prevents stretching/overflow in Outlook 2007-2019 and Outlook Classic
|
|
292
|
+
const borderStyleAttr = borderWidth > 0
|
|
293
|
+
? `border: ${borderWidth}px solid ${borderColor};`
|
|
294
|
+
: '';
|
|
295
|
+
outlookImage = `<!--[if mso]>
|
|
296
|
+
<table border="0" cellpadding="0" cellspacing="0" width="${vmlWidth}" style="width:${vmlWidth}px;">
|
|
297
|
+
<tr>
|
|
298
|
+
<td align="center" valign="top" width="${vmlWidth}" style="width:${vmlWidth}px;">
|
|
299
|
+
<img src="${imageUrl}" alt="Image" border="0" width="${vmlWidth}" height="${vmlHeight}" style="display:block; width:${vmlWidth}px; height:${vmlHeight}px; max-width:${vmlWidth}px; ${borderStyleAttr}" />
|
|
300
|
+
</td>
|
|
301
|
+
</tr>
|
|
302
|
+
</table>
|
|
260
303
|
<![endif]-->`;
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
304
|
+
}
|
|
305
|
+
return `
|
|
306
|
+
${outlookImage}
|
|
307
|
+
<!--[if !mso]><!-->
|
|
308
|
+
${content}
|
|
309
|
+
<!--<![endif]-->
|
|
266
310
|
`;
|
|
267
311
|
}
|
|
268
312
|
async function computeScaledDimensions(imageUrl, maxContainerWidthPx) {
|
|
@@ -279,42 +323,52 @@ async function convertImageBlock(blockData, cellWidthInPx) {
|
|
|
279
323
|
const { altText, imageUrl, navigateToUrl } = props;
|
|
280
324
|
const visibilityClass = (0, common_1.getVisibilityClass)(props);
|
|
281
325
|
const { width, height, objectFit, borderRadius, borderWidth, borderColor, borderStyle, ...containerStyle } = style;
|
|
282
|
-
// Ensure border styles are applied only to the container, not the image
|
|
283
|
-
const imageStyle = {
|
|
284
|
-
width,
|
|
285
|
-
height,
|
|
286
|
-
objectFit,
|
|
287
|
-
borderStyle,
|
|
288
|
-
borderRadius: borderRadius,
|
|
289
|
-
borderColor,
|
|
290
|
-
};
|
|
291
326
|
// Add border styles to container for fallback clients
|
|
292
327
|
const containerStyles = buildStyles({
|
|
293
328
|
...containerStyle,
|
|
294
329
|
}, { perChanges: [], pxChanges: addPxToAttributes });
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
330
|
+
// OUTLOOK FIX: Ensure cellWidthInPx never exceeds 600px
|
|
331
|
+
const safeCellWidth = Math.min(cellWidthInPx, 600);
|
|
332
|
+
// Parse width percentage (default 100%)
|
|
333
|
+
const widthPercent = typeof width === "string" && width.includes("%")
|
|
334
|
+
? parseInt(width.replace("%", ""))
|
|
335
|
+
: typeof width === "number"
|
|
336
|
+
? width
|
|
337
|
+
: 100;
|
|
338
|
+
// OUTLOOK FIX: Calculate inner container width based on safe cell width
|
|
339
|
+
const paddingLeft = style?.padding?.left || 0;
|
|
340
|
+
const paddingRight = style?.padding?.right || 0;
|
|
341
|
+
const availableWidth = safeCellWidth - paddingLeft - paddingRight;
|
|
342
|
+
const innerContainerWidth = Math.round((widthPercent / 100) * availableWidth);
|
|
343
|
+
// Get image dimensions and calculate scaled sizes
|
|
301
344
|
const { originalWidth, originalHeight, scaledWidth, scaledHeight } = await computeScaledDimensions(imageUrl, innerContainerWidth);
|
|
345
|
+
// OUTLOOK FIX: For Outlook, we need exact pixel dimensions
|
|
346
|
+
// Calculate final dimensions that respect both original size and container
|
|
347
|
+
const finalWidth = Math.min(scaledWidth, innerContainerWidth, originalWidth);
|
|
348
|
+
const finalHeight = Math.round((finalWidth / originalWidth) * originalHeight);
|
|
349
|
+
// Build image styles for modern email clients (non-Outlook)
|
|
302
350
|
const imageTagStyles = buildStyles({
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
351
|
+
borderStyle,
|
|
352
|
+
borderRadius: borderRadius,
|
|
353
|
+
borderColor,
|
|
354
|
+
borderWidth,
|
|
306
355
|
}, {
|
|
307
|
-
perChanges:
|
|
356
|
+
perChanges: [],
|
|
308
357
|
pxChanges: addPxToAttributes,
|
|
309
358
|
});
|
|
310
|
-
|
|
359
|
+
// OUTLOOK FIX: Image element with explicit dimensions
|
|
360
|
+
// Outlook will use width/height attributes, modern clients use CSS
|
|
361
|
+
// Use max-width instead of width:100% to prevent stretching
|
|
362
|
+
const imageElement = `<img src="${imageUrl}" alt="${altText || "Image"}" border="0" width="${finalWidth}" height="${finalHeight}" style="${imageTagStyles}; display:block; max-width:100%; height:auto;" />`;
|
|
311
363
|
const percentWidth = typeof width === "string" && width.endsWith("%")
|
|
312
364
|
? width
|
|
313
365
|
: typeof width === "number"
|
|
314
366
|
? `${width}%`
|
|
315
367
|
: "100%";
|
|
368
|
+
// Non-MSO wrapper for responsive behavior
|
|
316
369
|
const nonMsoWrapper = `<div style="display:inline-block; width:${percentWidth}; max-width:${originalWidth}px;">${imageElement}</div>`;
|
|
317
|
-
|
|
370
|
+
// OUTLOOK FIX: Generate VML with corrected dimensions
|
|
371
|
+
const outlookImage = await appendOutlookForImage(nonMsoWrapper, safeCellWidth, innerContainerWidth, imageUrl, style, finalWidth, finalHeight);
|
|
318
372
|
const imageContent = appendOutlookSupport(outlookImage, containerStyles, visibilityClass);
|
|
319
373
|
return navigateToUrl
|
|
320
374
|
? `<a href="${navigateToUrl}" target="_blank" rel="noreferrer noopener" style="display:block;">${imageContent}</a>`
|
|
@@ -325,23 +379,23 @@ function appendOutlookForButton(content, buttonStyle, navigateToUrl, text) {
|
|
|
325
379
|
const borderAttributes = borderWidth > 0
|
|
326
380
|
? `strokeweight="${borderWidth}px" strokecolor="${borderColor}"`
|
|
327
381
|
: `stroked="false"`;
|
|
328
|
-
return `
|
|
329
|
-
<!--[if mso]>
|
|
330
|
-
<v:${borderRadius ? "roundrect" : "rect"} xmlns:v="urn:schemas-microsoft-com:vml" href="${navigateToUrl}"
|
|
331
|
-
style="height:${height}px;v-text-anchor:middle;width:${width}px;"
|
|
332
|
-
arcsize="${borderRadius / height}" ${borderAttributes}
|
|
333
|
-
fillcolor="${buttonColor}">
|
|
334
|
-
<w:anchorlock/>
|
|
335
|
-
<v:textbox inset="${buttonPadding.top}px,${buttonPadding.left}px,${buttonPadding.bottom}px,${buttonPadding.right}px">
|
|
336
|
-
<center style="font-family:${fontFamily};font-size:${fontSize}px;font-weight:${fontWeight};color:${color};">
|
|
337
|
-
${text}
|
|
338
|
-
</center>
|
|
339
|
-
</v:textbox>
|
|
340
|
-
</v:${borderRadius ? "roundrect" : "rect"}>
|
|
341
|
-
<![endif]-->
|
|
342
|
-
<!--[if !mso]><!-->
|
|
343
|
-
${content}
|
|
344
|
-
<!--<![endif]-->
|
|
382
|
+
return `
|
|
383
|
+
<!--[if mso]>
|
|
384
|
+
<v:${borderRadius ? "roundrect" : "rect"} xmlns:v="urn:schemas-microsoft-com:vml" href="${navigateToUrl}"
|
|
385
|
+
style="height:${height}px;v-text-anchor:middle;width:${width}px;"
|
|
386
|
+
arcsize="${borderRadius / height}" ${borderAttributes}
|
|
387
|
+
fillcolor="${buttonColor}">
|
|
388
|
+
<w:anchorlock/>
|
|
389
|
+
<v:textbox inset="${buttonPadding.top}px,${buttonPadding.left}px,${buttonPadding.bottom}px,${buttonPadding.right}px">
|
|
390
|
+
<center style="font-family:${fontFamily};font-size:${fontSize}px;font-weight:${fontWeight};color:${color};">
|
|
391
|
+
${text}
|
|
392
|
+
</center>
|
|
393
|
+
</v:textbox>
|
|
394
|
+
</v:${borderRadius ? "roundrect" : "rect"}>
|
|
395
|
+
<![endif]-->
|
|
396
|
+
<!--[if !mso]><!-->
|
|
397
|
+
${content}
|
|
398
|
+
<!--<![endif]-->
|
|
345
399
|
`;
|
|
346
400
|
}
|
|
347
401
|
function convertButtonBlock(blockData) {
|
|
@@ -387,16 +441,20 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
|
|
|
387
441
|
});
|
|
388
442
|
const total = childrenIds.length;
|
|
389
443
|
const visualRows = Math.ceil(total / columns);
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
444
|
+
// OUTLOOK FIX: Use explicit pixel width for Old Outlook (Word engine)
|
|
445
|
+
const msoTableWidth = Math.min(cellWidthInPx, 600);
|
|
446
|
+
let html = `
|
|
447
|
+
<!--[if mso]>
|
|
448
|
+
<table border="0" cellpadding="0" cellspacing="${columnGap}" width="${msoTableWidth}"
|
|
449
|
+
style="border-collapse:separate;border-spacing:${columnGap}px;width:${msoTableWidth}px;"
|
|
450
|
+
class="${gridVisibilityClass}">
|
|
451
|
+
<![endif]-->
|
|
452
|
+
<!--[if !mso]><!-->
|
|
453
|
+
<table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%"
|
|
454
|
+
role="presentation"
|
|
455
|
+
style="border-collapse:separate;border-spacing:${columnGap}px; ${tableStyles}; max-width:600px;"
|
|
456
|
+
class="${gridVisibilityClass}">
|
|
457
|
+
<!--<![endif]-->
|
|
400
458
|
`;
|
|
401
459
|
for (let r = 0; r < visualRows; r++) {
|
|
402
460
|
html += "<tr>";
|
|
@@ -414,18 +472,38 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
|
|
|
414
472
|
visibleCells++;
|
|
415
473
|
}
|
|
416
474
|
}
|
|
417
|
-
// FIX:
|
|
418
|
-
|
|
475
|
+
// OUTLOOK FIX: Calculate safe width based on visible cells
|
|
476
|
+
// If we have visible cells, distribute 100% width evenly
|
|
477
|
+
const safeWidth = visibleCells > 0 ? 100 / visibleCells : 100 / columns;
|
|
478
|
+
// OUTLOOK FIX: Calculate total width used by all cells
|
|
479
|
+
let totalWidth = 0;
|
|
480
|
+
const cellWidthPercents = [];
|
|
419
481
|
for (let c = 0; c < columns; c++) {
|
|
420
|
-
const idx = r * columns + c;
|
|
421
482
|
const id = rowIds[c];
|
|
422
483
|
let widthPercent = cellWidths[c] ?? safeWidth;
|
|
423
|
-
//
|
|
484
|
+
// Validate width
|
|
424
485
|
if (widthPercent <= 0 || widthPercent > 100) {
|
|
425
486
|
widthPercent = safeWidth;
|
|
426
487
|
}
|
|
427
|
-
|
|
428
|
-
|
|
488
|
+
cellWidthPercents.push(widthPercent);
|
|
489
|
+
if (id) {
|
|
490
|
+
const child = rootData[id];
|
|
491
|
+
const isHidden = child?.data?.props?.hideOnDesktop;
|
|
492
|
+
if (!isHidden) {
|
|
493
|
+
totalWidth += widthPercent;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
// OUTLOOK FIX: If total width < 100%, scale up to fill container
|
|
498
|
+
// This prevents orphaned space that Outlook handles poorly
|
|
499
|
+
const scaleFactor = totalWidth > 0 && totalWidth < 100 ? 100 / totalWidth : 1;
|
|
500
|
+
for (let c = 0; c < columns; c++) {
|
|
501
|
+
const idx = r * columns + c;
|
|
502
|
+
const id = rowIds[c];
|
|
503
|
+
// OUTLOOK FIX: Scale width to ensure cells fill 100% of container
|
|
504
|
+
let widthPercent = cellWidthPercents[c] * scaleFactor;
|
|
505
|
+
// OUTLOOK FIX: Ensure width doesn't exceed 100% after scaling
|
|
506
|
+
widthPercent = Math.min(widthPercent, 100);
|
|
429
507
|
if (id) {
|
|
430
508
|
const child = rootData[id];
|
|
431
509
|
const { style: cellStyle = {}, props: childProps = {} } = child.data;
|
|
@@ -435,31 +513,43 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
|
|
|
435
513
|
// Only render if visible
|
|
436
514
|
if (childVisible) {
|
|
437
515
|
const { html: childHtml, styles } = await convertGridCellBlock(child, rootData, widthPercent, cellWidthInPx);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
516
|
+
// OUTLOOK FIX: Calculate pixel width for Outlook
|
|
517
|
+
const cellWidthPx = Math.round((widthPercent / 100) * cellWidthInPx);
|
|
518
|
+
html += `
|
|
519
|
+
<td
|
|
520
|
+
width="${Math.round(widthPercent)}%"
|
|
441
521
|
class="${[
|
|
442
522
|
responsive ? "stack-column" : "",
|
|
443
523
|
visibilityClass,
|
|
444
|
-
].filter(Boolean).join(" ")}"
|
|
445
|
-
style="vertical-align:${verticalAlign};word-break:break-word;${styles}"
|
|
446
|
-
>
|
|
447
|
-
${childHtml}
|
|
524
|
+
].filter(Boolean).join(" ")}"
|
|
525
|
+
style="width:${cellWidthPx}px;vertical-align:${verticalAlign};word-break:break-word;${styles}"
|
|
526
|
+
>
|
|
527
|
+
${childHtml}
|
|
448
528
|
</td>`;
|
|
449
529
|
}
|
|
450
530
|
}
|
|
451
531
|
else {
|
|
452
532
|
// SAFE empty cell (keeps layout stable)
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
533
|
+
// OUTLOOK FIX: Calculate pixel width for Outlook
|
|
534
|
+
const cellWidthPx = Math.round((widthPercent / 100) * cellWidthInPx);
|
|
535
|
+
html += `
|
|
536
|
+
<td width="${Math.round(widthPercent)}%"
|
|
537
|
+
${responsive ? 'class="stack-column"' : ""}
|
|
538
|
+
style="width:${cellWidthPx}px;vertical-align:top;">
|
|
457
539
|
</td>`;
|
|
458
540
|
}
|
|
459
541
|
}
|
|
460
542
|
html += "</tr>";
|
|
461
543
|
}
|
|
462
|
-
|
|
544
|
+
// Close both MSO and non-MSO tables
|
|
545
|
+
html += `
|
|
546
|
+
<!--[if mso]>
|
|
547
|
+
</table>
|
|
548
|
+
<![endif]-->
|
|
549
|
+
<!--[if !mso]><!-->
|
|
550
|
+
</table>
|
|
551
|
+
<!--<![endif]-->
|
|
552
|
+
`;
|
|
463
553
|
return html;
|
|
464
554
|
}
|
|
465
555
|
async function convertGridCellBlock(blockData, rootData, cellWidthPercent, parentCellWidthPx) {
|
|
@@ -470,8 +560,11 @@ async function convertGridCellBlock(blockData, rootData, cellWidthPercent, paren
|
|
|
470
560
|
pxChanges: allPxAttributes,
|
|
471
561
|
});
|
|
472
562
|
const parts = [];
|
|
473
|
-
// FIX:
|
|
474
|
-
|
|
563
|
+
// OUTLOOK FIX: Calculate the actual cell width in pixels based on percentage
|
|
564
|
+
// If parent is 600px and cell is 50%, cell width should be 300px, not 600px
|
|
565
|
+
const cellWidthPx = Math.round((cellWidthPercent / 100) * parentCellWidthPx);
|
|
566
|
+
// OUTLOOK FIX: Ensure cell width is reasonable and capped at 600px
|
|
567
|
+
const safeCellWidthPx = Math.min(Math.max(cellWidthPx, 20), 600);
|
|
475
568
|
for (const childId of childrenIds) {
|
|
476
569
|
const child = rootData[childId];
|
|
477
570
|
if (child) {
|
|
@@ -544,116 +637,116 @@ async function convertVideoBlock(blockData, cellWidthInPx) {
|
|
|
544
637
|
const vmlTop = calculatedHeight / 2 - playIconHeight / 2;
|
|
545
638
|
const shouldHideInOutlook = hideOnDesktop;
|
|
546
639
|
const outlookVideoContent = shouldHideInOutlook
|
|
547
|
-
? `<!--[if !mso]><!-->
|
|
548
|
-
<v:group xmlns:v="urn:schemas-microsoft-com:vml"
|
|
549
|
-
coordsize="${innerContainerWidth},${calculatedHeight}"
|
|
550
|
-
href="${videoLink}"
|
|
551
|
-
style="width:${innerContainerWidth}px;height:${calculatedHeight}px;">
|
|
552
|
-
<v:rect fill="t" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px;" stroked="t"
|
|
553
|
-
strokeweight="${borderWidth}px"
|
|
554
|
-
strokecolor="${borderColor}"
|
|
555
|
-
${borderRadius > 0 ? `arcsize="${Math.min(borderRadius / calculatedHeight, 1).toFixed(2)}"` : ""}
|
|
556
|
-
>
|
|
557
|
-
<v:fill src="${resolvedThumbnail}" type="frame" color="${style?.backgroundColor || "#FFFFFF"}"/>
|
|
558
|
-
</v:rect>
|
|
559
|
-
<v:shape type="#_x0000_t75"
|
|
560
|
-
style="position:absolute;
|
|
561
|
-
left:${vmlLeft.toFixed(1)}px;
|
|
562
|
-
top:${vmlTop.toFixed(1)}px;
|
|
563
|
-
width:${playIconWidth}px;
|
|
564
|
-
height:${playIconHeight}px;"
|
|
565
|
-
alt="Play" href="${videoLink}" title="${altText || "Video"}"
|
|
566
|
-
stroked="f" filled="t">
|
|
567
|
-
<v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
|
|
568
|
-
</v:shape>
|
|
569
|
-
</v:group>
|
|
640
|
+
? `<!--[if !mso]><!-->
|
|
641
|
+
<v:group xmlns:v="urn:schemas-microsoft-com:vml"
|
|
642
|
+
coordsize="${innerContainerWidth},${calculatedHeight}"
|
|
643
|
+
href="${videoLink}"
|
|
644
|
+
style="width:${innerContainerWidth}px;height:${calculatedHeight}px;">
|
|
645
|
+
<v:rect fill="t" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px;" stroked="t"
|
|
646
|
+
strokeweight="${borderWidth}px"
|
|
647
|
+
strokecolor="${borderColor}"
|
|
648
|
+
${borderRadius > 0 ? `arcsize="${Math.min(borderRadius / calculatedHeight, 1).toFixed(2)}"` : ""}
|
|
649
|
+
>
|
|
650
|
+
<v:fill src="${resolvedThumbnail}" type="frame" color="${style?.backgroundColor || "#FFFFFF"}"/>
|
|
651
|
+
</v:rect>
|
|
652
|
+
<v:shape type="#_x0000_t75"
|
|
653
|
+
style="position:absolute;
|
|
654
|
+
left:${vmlLeft.toFixed(1)}px;
|
|
655
|
+
top:${vmlTop.toFixed(1)}px;
|
|
656
|
+
width:${playIconWidth}px;
|
|
657
|
+
height:${playIconHeight}px;"
|
|
658
|
+
alt="Play" href="${videoLink}" title="${altText || "Video"}"
|
|
659
|
+
stroked="f" filled="t">
|
|
660
|
+
<v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
|
|
661
|
+
</v:shape>
|
|
662
|
+
</v:group>
|
|
570
663
|
<!--<![endif]-->`
|
|
571
|
-
: `<!--[if mso]>
|
|
572
|
-
<v:group xmlns:v="urn:schemas-microsoft-com:vml"
|
|
573
|
-
coordsize="${innerContainerWidth},${calculatedHeight}"
|
|
574
|
-
href="${videoLink}"
|
|
575
|
-
style="width:${innerContainerWidth}px;height:${calculatedHeight}px;">
|
|
576
|
-
<v:rect fill="t" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px; stroked="t"
|
|
577
|
-
strokeweight="${borderWidth}px"
|
|
578
|
-
strokecolor="${borderColor}"
|
|
579
|
-
${borderRadius > 0 ? `arcsize="${Math.min(borderRadius / calculatedHeight, 1).toFixed(2)}"` : ""}
|
|
580
|
-
>
|
|
581
|
-
<v:fill src="${resolvedThumbnail}" type="frame" color="${style?.backgroundColor || "#FFFFFF"}"/>
|
|
582
|
-
</v:rect>
|
|
583
|
-
<v:shape type="#_x0000_t75"
|
|
584
|
-
style="position:absolute;
|
|
585
|
-
left:${vmlLeft.toFixed(1)}px;
|
|
586
|
-
top:${vmlTop.toFixed(1)}px;
|
|
587
|
-
width:${playIconWidth}px;
|
|
588
|
-
height:${playIconHeight}px;"
|
|
589
|
-
alt="Play" href="${videoLink}" title="${altText || "Video"}"
|
|
590
|
-
stroked="f" filled="t">
|
|
591
|
-
<v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
|
|
592
|
-
</v:shape>
|
|
593
|
-
</v:group>
|
|
664
|
+
: `<!--[if mso]>
|
|
665
|
+
<v:group xmlns:v="urn:schemas-microsoft-com:vml"
|
|
666
|
+
coordsize="${innerContainerWidth},${calculatedHeight}"
|
|
667
|
+
href="${videoLink}"
|
|
668
|
+
style="width:${innerContainerWidth}px;height:${calculatedHeight}px;">
|
|
669
|
+
<v:rect fill="t" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px; stroked="t"
|
|
670
|
+
strokeweight="${borderWidth}px"
|
|
671
|
+
strokecolor="${borderColor}"
|
|
672
|
+
${borderRadius > 0 ? `arcsize="${Math.min(borderRadius / calculatedHeight, 1).toFixed(2)}"` : ""}
|
|
673
|
+
>
|
|
674
|
+
<v:fill src="${resolvedThumbnail}" type="frame" color="${style?.backgroundColor || "#FFFFFF"}"/>
|
|
675
|
+
</v:rect>
|
|
676
|
+
<v:shape type="#_x0000_t75"
|
|
677
|
+
style="position:absolute;
|
|
678
|
+
left:${vmlLeft.toFixed(1)}px;
|
|
679
|
+
top:${vmlTop.toFixed(1)}px;
|
|
680
|
+
width:${playIconWidth}px;
|
|
681
|
+
height:${playIconHeight}px;"
|
|
682
|
+
alt="Play" href="${videoLink}" title="${altText || "Video"}"
|
|
683
|
+
stroked="f" filled="t">
|
|
684
|
+
<v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
|
|
685
|
+
</v:shape>
|
|
686
|
+
</v:group>
|
|
594
687
|
<![endif]-->`;
|
|
595
|
-
const nonOutlookVideoContent = `<!--[if !mso]><!-->
|
|
596
|
-
<table
|
|
597
|
-
width="${innerContainerWidth}"
|
|
598
|
-
cellpadding="0"
|
|
599
|
-
cellspacing="0"
|
|
600
|
-
border="0"
|
|
601
|
-
role="presentation"
|
|
602
|
-
align="${style?.textAlign || "left"}"
|
|
603
|
-
style="
|
|
604
|
-
max-width: ${innerContainerWidth}px;
|
|
605
|
-
width: 100%;
|
|
606
|
-
height: ${calculatedHeight}px;
|
|
607
|
-
background-color: ${style?.backgroundColor || "#FFFFFF"};
|
|
608
|
-
background-image: url('${resolvedThumbnail}');
|
|
609
|
-
background-size: contain;
|
|
610
|
-
background-position: center;
|
|
611
|
-
background-repeat: no-repeat;
|
|
612
|
-
box-sizing: border-box;
|
|
613
|
-
border: ${borderWidth}px ${style?.borderStyle || "solid"} ${borderColor};
|
|
614
|
-
border-radius: ${borderRadius}px;
|
|
615
|
-
"
|
|
616
|
-
>
|
|
617
|
-
<tr>
|
|
618
|
-
<td style="padding: 0; height: ${calculatedHeight}px; text-align: center; vertical-align: middle;" valign="middle">
|
|
619
|
-
<a href="${videoLink}" target="_blank" style="display:inline-block; border: 0; outline: none; text-decoration: none;">
|
|
620
|
-
<img
|
|
621
|
-
src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png"
|
|
622
|
-
width="${playIconWidth}"
|
|
623
|
-
alt="Play"
|
|
624
|
-
style="display: block;
|
|
625
|
-
border: 0;
|
|
626
|
-
outline: none;
|
|
627
|
-
text-decoration: none;
|
|
628
|
-
height: auto;"
|
|
629
|
-
/>
|
|
630
|
-
</a>
|
|
631
|
-
</td>
|
|
632
|
-
</tr>
|
|
633
|
-
</table>
|
|
688
|
+
const nonOutlookVideoContent = `<!--[if !mso]><!-->
|
|
689
|
+
<table
|
|
690
|
+
width="${innerContainerWidth}"
|
|
691
|
+
cellpadding="0"
|
|
692
|
+
cellspacing="0"
|
|
693
|
+
border="0"
|
|
694
|
+
role="presentation"
|
|
695
|
+
align="${style?.textAlign || "left"}"
|
|
696
|
+
style="
|
|
697
|
+
max-width: ${innerContainerWidth}px;
|
|
698
|
+
width: 100%;
|
|
699
|
+
height: ${calculatedHeight}px;
|
|
700
|
+
background-color: ${style?.backgroundColor || "#FFFFFF"};
|
|
701
|
+
background-image: url('${resolvedThumbnail}');
|
|
702
|
+
background-size: contain;
|
|
703
|
+
background-position: center;
|
|
704
|
+
background-repeat: no-repeat;
|
|
705
|
+
box-sizing: border-box;
|
|
706
|
+
border: ${borderWidth}px ${style?.borderStyle || "solid"} ${borderColor};
|
|
707
|
+
border-radius: ${borderRadius}px;
|
|
708
|
+
"
|
|
709
|
+
>
|
|
710
|
+
<tr>
|
|
711
|
+
<td style="padding: 0; height: ${calculatedHeight}px; text-align: center; vertical-align: middle;" valign="middle">
|
|
712
|
+
<a href="${videoLink}" target="_blank" style="display:inline-block; border: 0; outline: none; text-decoration: none;">
|
|
713
|
+
<img
|
|
714
|
+
src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png"
|
|
715
|
+
width="${playIconWidth}"
|
|
716
|
+
alt="Play"
|
|
717
|
+
style="display: block;
|
|
718
|
+
border: 0;
|
|
719
|
+
outline: none;
|
|
720
|
+
text-decoration: none;
|
|
721
|
+
height: auto;"
|
|
722
|
+
/>
|
|
723
|
+
</a>
|
|
724
|
+
</td>
|
|
725
|
+
</tr>
|
|
726
|
+
</table>
|
|
634
727
|
<!--<![endif]-->`;
|
|
635
728
|
const videoContent = `${outlookVideoContent}${nonOutlookVideoContent}`;
|
|
636
|
-
const wrapperHtml = `
|
|
637
|
-
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin:0; padding:0; border-collapse: collapse; max-width:600px;" class="${visibilityClass}">
|
|
638
|
-
<tr>
|
|
639
|
-
<td align="${style?.textAlign || "left"}" style="padding:0; ${outerContainerStyles}">
|
|
640
|
-
<table border="0" cellpadding="0" cellspacing="0" role="presentation"
|
|
641
|
-
align="${style?.textAlign || "left"}"
|
|
642
|
-
style="
|
|
643
|
-
margin:0;
|
|
644
|
-
max-width:${cellWidthInPx}px;
|
|
645
|
-
width:${percentWidth};
|
|
646
|
-
border-collapse:collapse;
|
|
647
|
-
">
|
|
648
|
-
<tr>
|
|
649
|
-
<td align="${style?.textAlign || "left"}" style="text-align:${style?.textAlign || "left"}; padding:0;">
|
|
650
|
-
${videoContent}
|
|
651
|
-
</td>
|
|
652
|
-
</tr>
|
|
653
|
-
</table>
|
|
654
|
-
</td>
|
|
655
|
-
</tr>
|
|
656
|
-
</table>
|
|
729
|
+
const wrapperHtml = `
|
|
730
|
+
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin:0; padding:0; border-collapse: collapse; max-width:600px;" class="${visibilityClass}">
|
|
731
|
+
<tr>
|
|
732
|
+
<td align="${style?.textAlign || "left"}" style="padding:0; ${outerContainerStyles}">
|
|
733
|
+
<table border="0" cellpadding="0" cellspacing="0" role="presentation"
|
|
734
|
+
align="${style?.textAlign || "left"}"
|
|
735
|
+
style="
|
|
736
|
+
margin:0;
|
|
737
|
+
max-width:${cellWidthInPx}px;
|
|
738
|
+
width:${percentWidth};
|
|
739
|
+
border-collapse:collapse;
|
|
740
|
+
">
|
|
741
|
+
<tr>
|
|
742
|
+
<td align="${style?.textAlign || "left"}" style="text-align:${style?.textAlign || "left"}; padding:0;">
|
|
743
|
+
${videoContent}
|
|
744
|
+
</td>
|
|
745
|
+
</tr>
|
|
746
|
+
</table>
|
|
747
|
+
</td>
|
|
748
|
+
</tr>
|
|
749
|
+
</table>
|
|
657
750
|
`;
|
|
658
751
|
return wrapperHtml;
|
|
659
752
|
}
|
|
@@ -735,44 +828,44 @@ async function convertShapeBlock(blockData) {
|
|
|
735
828
|
let nonMsoContent = "";
|
|
736
829
|
// --- Case 1: Image + Text ---
|
|
737
830
|
if (imageUrl && text) {
|
|
738
|
-
nonMsoContent = `
|
|
739
|
-
<div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
740
|
-
border:${borderWidth}px ${borderStyle} ${borderColor};
|
|
741
|
-
border-radius:${resolvedBorderRadius};
|
|
742
|
-
background:${finalBackgroundColor} url('${imageUrl}') center/cover no-repeat;
|
|
743
|
-
overflow:hidden;${alignmentStyle}${customCss || ""}">
|
|
744
|
-
<div style="width:100%;height:100%;display:flex;justify-content:${flexJustify};align-items:${flexAlign};overflow:hidden;padding:6px;box-sizing:border-box;">
|
|
745
|
-
<div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">
|
|
746
|
-
${text}
|
|
747
|
-
</div>
|
|
748
|
-
</div>
|
|
831
|
+
nonMsoContent = `
|
|
832
|
+
<div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
833
|
+
border:${borderWidth}px ${borderStyle} ${borderColor};
|
|
834
|
+
border-radius:${resolvedBorderRadius};
|
|
835
|
+
background:${finalBackgroundColor} url('${imageUrl}') center/cover no-repeat;
|
|
836
|
+
overflow:hidden;${alignmentStyle}${customCss || ""}">
|
|
837
|
+
<div style="width:100%;height:100%;display:flex;justify-content:${flexJustify};align-items:${flexAlign};overflow:hidden;padding:6px;box-sizing:border-box;">
|
|
838
|
+
<div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">
|
|
839
|
+
${text}
|
|
840
|
+
</div>
|
|
841
|
+
</div>
|
|
749
842
|
</div>`;
|
|
750
843
|
}
|
|
751
844
|
// --- Case 2: Image only ---
|
|
752
845
|
else if (imageUrl) {
|
|
753
|
-
nonMsoContent = `
|
|
754
|
-
<div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
755
|
-
border:${borderWidth}px ${borderStyle} ${borderColor};
|
|
756
|
-
border-radius:${resolvedBorderRadius};
|
|
757
|
-
overflow:hidden;${alignmentStyle}${customCss || ""}">
|
|
758
|
-
<img src="${imageUrl}" alt="${text || "shape image"}"
|
|
759
|
-
width="${resolvedWidthPx}" height="${resolvedHeightPx}"
|
|
760
|
-
style="width:100%;height:100%;object-fit:cover;border-radius:${resolvedBorderRadius};display:block;" />
|
|
846
|
+
nonMsoContent = `
|
|
847
|
+
<div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
848
|
+
border:${borderWidth}px ${borderStyle} ${borderColor};
|
|
849
|
+
border-radius:${resolvedBorderRadius};
|
|
850
|
+
overflow:hidden;${alignmentStyle}${customCss || ""}">
|
|
851
|
+
<img src="${imageUrl}" alt="${text || "shape image"}"
|
|
852
|
+
width="${resolvedWidthPx}" height="${resolvedHeightPx}"
|
|
853
|
+
style="width:100%;height:100%;object-fit:cover;border-radius:${resolvedBorderRadius};display:block;" />
|
|
761
854
|
</div>`;
|
|
762
855
|
}
|
|
763
856
|
// --- Case 3: Text only ---
|
|
764
857
|
else {
|
|
765
|
-
nonMsoContent = `
|
|
766
|
-
<div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
767
|
-
background:${finalBackgroundColor};
|
|
768
|
-
border:${borderWidth}px ${borderStyle} ${borderColor};
|
|
769
|
-
border-radius:${resolvedBorderRadius};
|
|
770
|
-
overflow:hidden;${alignmentStyle}${customCss || ""}">
|
|
771
|
-
<div style="width:100%;height:100%;display:flex;justify-content:${flexJustify};align-items:${flexAlign};padding:8px;box-sizing:border-box;">
|
|
772
|
-
<div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">
|
|
773
|
-
${text || ""}
|
|
774
|
-
</div>
|
|
775
|
-
</div>
|
|
858
|
+
nonMsoContent = `
|
|
859
|
+
<div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
860
|
+
background:${finalBackgroundColor};
|
|
861
|
+
border:${borderWidth}px ${borderStyle} ${borderColor};
|
|
862
|
+
border-radius:${resolvedBorderRadius};
|
|
863
|
+
overflow:hidden;${alignmentStyle}${customCss || ""}">
|
|
864
|
+
<div style="width:100%;height:100%;display:flex;justify-content:${flexJustify};align-items:${flexAlign};padding:8px;box-sizing:border-box;">
|
|
865
|
+
<div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">
|
|
866
|
+
${text || ""}
|
|
867
|
+
</div>
|
|
868
|
+
</div>
|
|
776
869
|
</div>`;
|
|
777
870
|
}
|
|
778
871
|
// Outlook (VML) fallback
|
|
@@ -795,16 +888,16 @@ async function convertShapeBlock(blockData) {
|
|
|
795
888
|
msoBakeImageWithText,
|
|
796
889
|
}, visibilityClass);
|
|
797
890
|
// Combine into table wrapper
|
|
798
|
-
return `
|
|
799
|
-
<table width="100%" style="border-collapse:collapse;table-layout:fixed;max-width:600px;" class="${visibilityClass}">
|
|
800
|
-
<tr>
|
|
801
|
-
<td style="padding:${padding.top || 0}px ${padding.right || 0}px ${padding.bottom || 0}px ${padding.left || 0}px;text-align:${alignment};">
|
|
802
|
-
${outlookContent}
|
|
803
|
-
<!--[if !mso]><!-->
|
|
804
|
-
${nonMsoContent}
|
|
805
|
-
<!--<![endif]-->
|
|
806
|
-
</td>
|
|
807
|
-
</tr>
|
|
891
|
+
return `
|
|
892
|
+
<table width="100%" style="border-collapse:collapse;table-layout:fixed;max-width:600px;" class="${visibilityClass}">
|
|
893
|
+
<tr>
|
|
894
|
+
<td style="padding:${padding.top || 0}px ${padding.right || 0}px ${padding.bottom || 0}px ${padding.left || 0}px;text-align:${alignment};">
|
|
895
|
+
${outlookContent}
|
|
896
|
+
<!--[if !mso]><!-->
|
|
897
|
+
${nonMsoContent}
|
|
898
|
+
<!--<![endif]-->
|
|
899
|
+
</td>
|
|
900
|
+
</tr>
|
|
808
901
|
</table>`;
|
|
809
902
|
}
|
|
810
903
|
// ---------- Updated VML builder ----------
|
|
@@ -837,24 +930,24 @@ function buildVMLShape({ shape, widthPx, heightPx, imageUrl, backgroundColor, bo
|
|
|
837
930
|
const safeFontSize = Math.max(Math.round(textSize), 10);
|
|
838
931
|
// Build the textbox with table/cell for reliable vertical centering in Outlook
|
|
839
932
|
const textboxMarkup = text && !msoHasBakedText
|
|
840
|
-
? `<v:textbox inset="6pt,6pt,6pt,6pt" style="mso-fit-shape-to-text:false;">
|
|
841
|
-
<div style="display:table;width:100%;height:100%;">
|
|
842
|
-
<div style="display:table-cell;vertical-align:${vAlign};text-align:${hAlign};padding:0 6px;">
|
|
843
|
-
<div style="color:${textColor};font-family:Arial, sans-serif;font-size:${safeFontSize}px;line-height:1.3;word-wrap:break-word;">
|
|
844
|
-
${text}
|
|
845
|
-
</div>
|
|
846
|
-
</div>
|
|
847
|
-
</div>
|
|
933
|
+
? `<v:textbox inset="6pt,6pt,6pt,6pt" style="mso-fit-shape-to-text:false;">
|
|
934
|
+
<div style="display:table;width:100%;height:100%;">
|
|
935
|
+
<div style="display:table-cell;vertical-align:${vAlign};text-align:${hAlign};padding:0 6px;">
|
|
936
|
+
<div style="color:${textColor};font-family:Arial, sans-serif;font-size:${safeFontSize}px;line-height:1.3;word-wrap:break-word;">
|
|
937
|
+
${text}
|
|
938
|
+
</div>
|
|
939
|
+
</div>
|
|
940
|
+
</div>
|
|
848
941
|
</v:textbox>`
|
|
849
942
|
: `<v:textbox inset="0,0,0,0"><div style="display:none;">.</div></v:textbox>`;
|
|
850
943
|
// Return VML shape
|
|
851
|
-
return `
|
|
852
|
-
<v:${tag} xmlns:v="urn:schemas-microsoft-com:vml"
|
|
853
|
-
style="width:${widthPx}px;height:${heightPx}px;display:inline-block;"
|
|
854
|
-
${borderAttrs}
|
|
855
|
-
fill="true" fillcolor="${fillColor}"${extraAttr}>
|
|
856
|
-
${fillMarkup}
|
|
857
|
-
${textboxMarkup}
|
|
944
|
+
return `
|
|
945
|
+
<v:${tag} xmlns:v="urn:schemas-microsoft-com:vml"
|
|
946
|
+
style="width:${widthPx}px;height:${heightPx}px;display:inline-block;"
|
|
947
|
+
${borderAttrs}
|
|
948
|
+
fill="true" fillcolor="${fillColor}"${extraAttr}>
|
|
949
|
+
${fillMarkup}
|
|
950
|
+
${textboxMarkup}
|
|
858
951
|
</v:${tag}>`;
|
|
859
952
|
}
|
|
860
953
|
function appendOutlookForShape(content, outerContainerWidth, innerContainerWidth, opts, visibilityClass) {
|
|
@@ -882,28 +975,28 @@ function appendOutlookForShape(content, outerContainerWidth, innerContainerWidth
|
|
|
882
975
|
const shouldHideInOutlook = visibilityClass.includes("hide-desktop");
|
|
883
976
|
// Fix: Properly handle Outlook visibility with conditional comments
|
|
884
977
|
if (shouldHideInOutlook) {
|
|
885
|
-
return `<!--[if !mso]><!-->
|
|
886
|
-
<table align="${align}" border="0" cellpadding="0" cellspacing="0"
|
|
887
|
-
style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
|
|
888
|
-
<tr>
|
|
889
|
-
<td valign="${valign}"
|
|
890
|
-
style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
|
|
891
|
-
${vml}
|
|
892
|
-
</td>
|
|
893
|
-
</tr>
|
|
894
|
-
</table>
|
|
978
|
+
return `<!--[if !mso]><!-->
|
|
979
|
+
<table align="${align}" border="0" cellpadding="0" cellspacing="0"
|
|
980
|
+
style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
|
|
981
|
+
<tr>
|
|
982
|
+
<td valign="${valign}"
|
|
983
|
+
style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
|
|
984
|
+
${vml}
|
|
985
|
+
</td>
|
|
986
|
+
</tr>
|
|
987
|
+
</table>
|
|
895
988
|
<!--<![endif]-->`;
|
|
896
989
|
}
|
|
897
|
-
return `<!--[if mso]>
|
|
898
|
-
<table align="${align}" border="0" cellpadding="0" cellspacing="0"
|
|
899
|
-
style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
|
|
900
|
-
<tr>
|
|
901
|
-
<td valign="${valign}"
|
|
902
|
-
style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
|
|
903
|
-
${vml}
|
|
904
|
-
</td>
|
|
905
|
-
</tr>
|
|
906
|
-
</table>
|
|
990
|
+
return `<!--[if mso]>
|
|
991
|
+
<table align="${align}" border="0" cellpadding="0" cellspacing="0"
|
|
992
|
+
style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
|
|
993
|
+
<tr>
|
|
994
|
+
<td valign="${valign}"
|
|
995
|
+
style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
|
|
996
|
+
${vml}
|
|
997
|
+
</td>
|
|
998
|
+
</tr>
|
|
999
|
+
</table>
|
|
907
1000
|
<![endif]-->`;
|
|
908
1001
|
}
|
|
909
1002
|
function convertVerticalDividerBlockToHtml(blockData) {
|
|
@@ -916,19 +1009,19 @@ function convertVerticalDividerBlockToHtml(blockData) {
|
|
|
916
1009
|
pxChanges: allPxAttributes,
|
|
917
1010
|
});
|
|
918
1011
|
// Outlook-safe vertical divider
|
|
919
|
-
const dividerContent = `
|
|
920
|
-
<table cellpadding="0" cellspacing="0" border="0" align="center" style="width:auto; ${convertedStyle}">
|
|
921
|
-
<tr>
|
|
922
|
-
<td style="vertical-align: middle; text-align: center;">
|
|
923
|
-
<!--[if mso | IE]>
|
|
924
|
-
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fillcolor="${dividerColor}" style="width:${width}px;height:${height}px;" stroke="f"></v:rect>
|
|
925
|
-
<![endif]-->
|
|
926
|
-
<!--[if !mso]><!-- -->
|
|
927
|
-
<div style="display:inline-block;width:${width}px;height:${height}px;background:${dividerColor};line-height:0;font-size:0;"> </div>
|
|
928
|
-
<!--<![endif]-->
|
|
929
|
-
</td>
|
|
930
|
-
</tr>
|
|
931
|
-
</table>
|
|
1012
|
+
const dividerContent = `
|
|
1013
|
+
<table cellpadding="0" cellspacing="0" border="0" align="center" style="width:auto; ${convertedStyle}">
|
|
1014
|
+
<tr>
|
|
1015
|
+
<td style="vertical-align: middle; text-align: center;">
|
|
1016
|
+
<!--[if mso | IE]>
|
|
1017
|
+
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fillcolor="${dividerColor}" style="width:${width}px;height:${height}px;" stroke="f"></v:rect>
|
|
1018
|
+
<![endif]-->
|
|
1019
|
+
<!--[if !mso]><!-- -->
|
|
1020
|
+
<div style="display:inline-block;width:${width}px;height:${height}px;background:${dividerColor};line-height:0;font-size:0;"> </div>
|
|
1021
|
+
<!--<![endif]-->
|
|
1022
|
+
</td>
|
|
1023
|
+
</tr>
|
|
1024
|
+
</table>
|
|
932
1025
|
`;
|
|
933
1026
|
return appendOutlookSupport(dividerContent, convertedStyle, visibilityClass);
|
|
934
1027
|
}
|
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.40",
|
|
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
|
+
}
|