email-builder-utils 1.1.34 → 1.1.35

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,GAAU,UAAU,GAAG,oBA4GpD,CAAC"}
1
+ {"version":3,"file":"convertJsonToHtml.d.ts","sourceRoot":"","sources":["../../src/utils/convertJsonToHtml.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,GAAU,UAAU,GAAG,oBA8GpD,CAAC"}
@@ -12,83 +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
- background-color: ${canvasColor};
77
- color: ${textColor};
78
- padding: ${top}px ${right}px ${bottom}px ${left}px;
79
- border: ${borderWidth}px ${borderStyle} ${borderColor};
80
- border-radius: ${borderRadius}px; "
81
- >
82
- <tbody>
83
- <tr>
84
- <td style="padding: 0;">
85
- ${blocksHtml.join("")}
86
- </td>
87
- </tr>
88
- </tbody>
89
- </table>
90
- </center>
91
- </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>
92
94
  </html>`;
93
95
  return rawHtml;
94
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;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;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;AAyFhF,wBAAsB,aAAa,CACjC,SAAS,EAAE,UAAU,EACrB,QAAQ,EAAE,GAAG,EACb,aAAa,EAAE,MAAM,mBAwBtB;AAikBD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAyL5E"}
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;AAyFhF,wBAAsB,aAAa,CACjC,SAAS,EAAE,UAAU,EACrB,QAAQ,EAAE,GAAG,EACb,aAAa,EAAE,MAAM,mBAwBtB;AAmkBD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAyL5E"}
@@ -127,14 +127,14 @@ 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
- return `
137
- <table width="100%" style="${exports.tableCommonStyle}" class="${visibilityClass}"><tr><td style="${contentStyle}">${content}</td></tr></table>
136
+ return `
137
+ <table width="100%" style="${exports.tableCommonStyle}; max-width:600px;" class="${visibilityClass}"><tr><td style="${contentStyle}">${content}</td></tr></table>
138
138
  `;
139
139
  }
140
140
  function convertDividerBlockToHtml(blockData) {
@@ -153,21 +153,21 @@ function convertDividerBlockToHtml(blockData) {
153
153
  ]
154
154
  .filter(Boolean)
155
155
  .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
- &nbsp;
168
- </td>
169
- </tr>
170
- </table>
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
+ &nbsp;
168
+ </td>
169
+ </tr>
170
+ </table>
171
171
  `;
172
172
  return appendOutlookSupport(dividerContent, convertedStyle, visibilityClass);
173
173
  }
@@ -248,21 +248,21 @@ async function appendOutlookForImage(content, outerContainerWidth, innerContaine
248
248
  const borderAttributes = borderWidth > 0
249
249
  ? `strokeweight="${borderWidth}px" strokecolor="${borderColor}"`
250
250
  : `stroked="false"`;
251
- const outlookImage = `<!--[if mso]>
252
- <v:${useRoundRect ? "roundrect" : "rect"} xmlns:v="urn:schemas-microsoft-com:vml"
253
- style="width:${scaledWidth}px;height:${scaledHeight}px;"
254
- ${borderAttributes}
255
- ${useRoundRect ? `arcsize="${arcsize}"` : ""}
256
- fill="true" fillcolor="none">
257
- <v:fill src="${imageUrl}" type="frame" />
258
- <v:textbox inset="0,0,0,0"><div style="display:none;">.</div></v:textbox>
259
- </v:${useRoundRect ? "roundrect" : "rect"}>
251
+ const outlookImage = `<!--[if mso]>
252
+ <v:${useRoundRect ? "roundrect" : "rect"} xmlns:v="urn:schemas-microsoft-com:vml"
253
+ style="width:${scaledWidth}px;height:${scaledHeight}px;"
254
+ ${borderAttributes}
255
+ ${useRoundRect ? `arcsize="${arcsize}"` : ""}
256
+ fill="true" fillcolor="none">
257
+ <v:fill src="${imageUrl}" type="frame" />
258
+ <v:textbox inset="0,0,0,0"><div style="display:none;">.</div></v:textbox>
259
+ </v:${useRoundRect ? "roundrect" : "rect"}>
260
260
  <![endif]-->`;
261
- return `
262
- ${outlookImage}
263
- <!--[if !mso]><!-->
264
- ${content}
265
- <!--<![endif]-->
261
+ return `
262
+ ${outlookImage}
263
+ <!--[if !mso]><!-->
264
+ ${content}
265
+ <!--<![endif]-->
266
266
  `;
267
267
  }
268
268
  async function computeScaledDimensions(imageUrl, maxContainerWidthPx) {
@@ -325,23 +325,23 @@ function appendOutlookForButton(content, buttonStyle, navigateToUrl, text) {
325
325
  const borderAttributes = borderWidth > 0
326
326
  ? `strokeweight="${borderWidth}px" strokecolor="${borderColor}"`
327
327
  : `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]-->
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]-->
345
345
  `;
346
346
  }
347
347
  function convertButtonBlock(blockData) {
@@ -387,16 +387,16 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
387
387
  });
388
388
  const total = childrenIds.length;
389
389
  const visualRows = Math.ceil(total / columns);
390
- let html = `
391
- <!--[if mso]>
392
- <table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%"
393
- style="border-collapse:separate;border-spacing:${columnGap}px;"
394
- class="${gridVisibilityClass}">
395
- <![endif]-->
396
- <table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%"
397
- role="presentation"
398
- style="border-collapse:separate;border-spacing:${columnGap}px; ${tableStyles}"
399
- class="${gridVisibilityClass}">
390
+ let html = `
391
+ <!--[if mso]>
392
+ <table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%"
393
+ style="border-collapse:separate;border-spacing:${columnGap}px;"
394
+ class="${gridVisibilityClass}">
395
+ <![endif]-->
396
+ <table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%"
397
+ role="presentation"
398
+ style="border-collapse:separate;border-spacing:${columnGap}px; ${tableStyles}; max-width:600px;"
399
+ class="${gridVisibilityClass}">
400
400
  `;
401
401
  for (let r = 0; r < visualRows; r++) {
402
402
  html += "<tr>";
@@ -415,7 +415,7 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
415
415
  }
416
416
  }
417
417
  // FIX: fallback safe-width
418
- const safeWidth = visibleCells > 0 ? 100 / visibleCells : 100 / columns;
418
+ const safeWidth = visibleCells > 0 ? Math.min(100 / visibleCells, 50) : Math.min(100 / columns, 50);
419
419
  for (let c = 0; c < columns; c++) {
420
420
  const idx = r * columns + c;
421
421
  const id = rowIds[c];
@@ -424,6 +424,8 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
424
424
  if (widthPercent <= 0 || widthPercent > 100) {
425
425
  widthPercent = safeWidth;
426
426
  }
427
+ // FIX: Cap width percent to prevent overflow in Outlook
428
+ widthPercent = Math.min(widthPercent, 50);
427
429
  if (id) {
428
430
  const child = rootData[id];
429
431
  const { style: cellStyle = {}, props: childProps = {} } = child.data;
@@ -433,25 +435,25 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
433
435
  // Only render if visible
434
436
  if (childVisible) {
435
437
  const { html: childHtml, styles } = await convertGridCellBlock(child, rootData, widthPercent, cellWidthInPx);
436
- html += `
437
- <td
438
- width="${Math.round(widthPercent)}%"
438
+ html += `
439
+ <td
440
+ width="${Math.round(widthPercent)}%"
439
441
  class="${[
440
442
  responsive ? "stack-column" : "",
441
443
  visibilityClass,
442
- ].filter(Boolean).join(" ")}"
443
- style="vertical-align:${verticalAlign};word-break:break-word;${styles}"
444
- >
445
- ${childHtml}
444
+ ].filter(Boolean).join(" ")}"
445
+ style="vertical-align:${verticalAlign};word-break:break-word;${styles}"
446
+ >
447
+ ${childHtml}
446
448
  </td>`;
447
449
  }
448
450
  }
449
451
  else {
450
452
  // SAFE empty cell (keeps layout stable)
451
- html += `
452
- <td width="${Math.round(widthPercent)}%"
453
- ${responsive ? 'class="stack-column"' : ""}
454
- style="vertical-align:top;">
453
+ html += `
454
+ <td width="${Math.round(widthPercent)}%"
455
+ ${responsive ? 'class="stack-column"' : ""}
456
+ style="vertical-align:top;">
455
457
  </td>`;
456
458
  }
457
459
  }
@@ -542,116 +544,116 @@ async function convertVideoBlock(blockData, cellWidthInPx) {
542
544
  const vmlTop = calculatedHeight / 2 - playIconHeight / 2;
543
545
  const shouldHideInOutlook = hideOnDesktop;
544
546
  const outlookVideoContent = shouldHideInOutlook
545
- ? `<!--[if !mso]><!-->
546
- <v:group xmlns:v="urn:schemas-microsoft-com:vml"
547
- coordsize="${innerContainerWidth},${calculatedHeight}"
548
- href="${videoLink}"
549
- style="width:${innerContainerWidth}px;height:${calculatedHeight}px;">
550
- <v:rect fill="t" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px;" stroked="t"
551
- strokeweight="${borderWidth}px"
552
- strokecolor="${borderColor}"
553
- ${borderRadius > 0 ? `arcsize="${Math.min(borderRadius / calculatedHeight, 1).toFixed(2)}"` : ""}
554
- >
555
- <v:fill src="${resolvedThumbnail}" type="frame" color="${style?.backgroundColor || "#FFFFFF"}"/>
556
- </v:rect>
557
- <v:shape type="#_x0000_t75"
558
- style="position:absolute;
559
- left:${vmlLeft.toFixed(1)}px;
560
- top:${vmlTop.toFixed(1)}px;
561
- width:${playIconWidth}px;
562
- height:${playIconHeight}px;"
563
- alt="Play" href="${videoLink}" title="${altText || "Video"}"
564
- stroked="f" filled="t">
565
- <v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
566
- </v:shape>
567
- </v:group>
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>
568
570
  <!--<![endif]-->`
569
- : `<!--[if mso]>
570
- <v:group xmlns:v="urn:schemas-microsoft-com:vml"
571
- coordsize="${innerContainerWidth},${calculatedHeight}"
572
- href="${videoLink}"
573
- style="width:${innerContainerWidth}px;height:${calculatedHeight}px;">
574
- <v:rect fill="t" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px; stroked="t"
575
- strokeweight="${borderWidth}px"
576
- strokecolor="${borderColor}"
577
- ${borderRadius > 0 ? `arcsize="${Math.min(borderRadius / calculatedHeight, 1).toFixed(2)}"` : ""}
578
- >
579
- <v:fill src="${resolvedThumbnail}" type="frame" color="${style?.backgroundColor || "#FFFFFF"}"/>
580
- </v:rect>
581
- <v:shape type="#_x0000_t75"
582
- style="position:absolute;
583
- left:${vmlLeft.toFixed(1)}px;
584
- top:${vmlTop.toFixed(1)}px;
585
- width:${playIconWidth}px;
586
- height:${playIconHeight}px;"
587
- alt="Play" href="${videoLink}" title="${altText || "Video"}"
588
- stroked="f" filled="t">
589
- <v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
590
- </v:shape>
591
- </v:group>
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>
592
594
  <![endif]-->`;
593
- const nonOutlookVideoContent = `<!--[if !mso]><!-->
594
- <table
595
- width="${innerContainerWidth}"
596
- cellpadding="0"
597
- cellspacing="0"
598
- border="0"
599
- role="presentation"
600
- align="${style?.textAlign || "left"}"
601
- style="
602
- max-width: ${innerContainerWidth}px;
603
- width: 100%;
604
- height: ${calculatedHeight}px;
605
- background-color: ${style?.backgroundColor || "#FFFFFF"};
606
- background-image: url('${resolvedThumbnail}');
607
- background-size: cover;
608
- background-position: center;
609
- background-repeat: no-repeat;
610
- box-sizing: border-box;
611
- border: ${borderWidth}px ${style?.borderStyle || "solid"} ${borderColor};
612
- border-radius: ${borderRadius}px;
613
- "
614
- >
615
- <tr>
616
- <td style="padding: 0; height: ${calculatedHeight}px; text-align: center; vertical-align: middle;" valign="middle">
617
- <a href="${videoLink}" target="_blank" style="display:inline-block; border: 0; outline: none; text-decoration: none;">
618
- <img
619
- src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png"
620
- width="${playIconWidth}"
621
- alt="Play"
622
- style="display: block;
623
- border: 0;
624
- outline: none;
625
- text-decoration: none;
626
- height: auto;"
627
- />
628
- </a>
629
- </td>
630
- </tr>
631
- </table>
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>
632
634
  <!--<![endif]-->`;
633
635
  const videoContent = `${outlookVideoContent}${nonOutlookVideoContent}`;
634
- const wrapperHtml = `
635
- <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin:0; padding:0; border-collapse: collapse;" class="${visibilityClass}">
636
- <tr>
637
- <td align="${style?.textAlign || "left"}" style="padding:0; ${outerContainerStyles}">
638
- <table border="0" cellpadding="0" cellspacing="0" role="presentation"
639
- align="${style?.textAlign || "left"}"
640
- style="
641
- margin:0;
642
- max-width:${cellWidthInPx}px;
643
- width:${percentWidth};
644
- border-collapse:collapse;
645
- ">
646
- <tr>
647
- <td align="${style?.textAlign || "left"}" style="text-align:${style?.textAlign || "left"}; padding:0;">
648
- ${videoContent}
649
- </td>
650
- </tr>
651
- </table>
652
- </td>
653
- </tr>
654
- </table>
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>
655
657
  `;
656
658
  return wrapperHtml;
657
659
  }
@@ -733,44 +735,44 @@ async function convertShapeBlock(blockData) {
733
735
  let nonMsoContent = "";
734
736
  // --- Case 1: Image + Text ---
735
737
  if (imageUrl && text) {
736
- nonMsoContent = `
737
- <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
738
- border:${borderWidth}px ${borderStyle} ${borderColor};
739
- border-radius:${resolvedBorderRadius};
740
- background:${finalBackgroundColor} url('${imageUrl}') center/cover no-repeat;
741
- overflow:hidden;${alignmentStyle}${customCss || ""}">
742
- <div style="width:100%;height:100%;display:flex;justify-content:${flexJustify};align-items:${flexAlign};overflow:hidden;padding:6px;box-sizing:border-box;">
743
- <div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">
744
- ${text}
745
- </div>
746
- </div>
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>
747
749
  </div>`;
748
750
  }
749
751
  // --- Case 2: Image only ---
750
752
  else if (imageUrl) {
751
- nonMsoContent = `
752
- <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
753
- border:${borderWidth}px ${borderStyle} ${borderColor};
754
- border-radius:${resolvedBorderRadius};
755
- overflow:hidden;${alignmentStyle}${customCss || ""}">
756
- <img src="${imageUrl}" alt="${text || "shape image"}"
757
- width="${resolvedWidthPx}" height="${resolvedHeightPx}"
758
- style="width:100%;height:100%;object-fit:cover;border-radius:${resolvedBorderRadius};display:block;" />
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;" />
759
761
  </div>`;
760
762
  }
761
763
  // --- Case 3: Text only ---
762
764
  else {
763
- nonMsoContent = `
764
- <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
765
- background:${finalBackgroundColor};
766
- border:${borderWidth}px ${borderStyle} ${borderColor};
767
- border-radius:${resolvedBorderRadius};
768
- overflow:hidden;${alignmentStyle}${customCss || ""}">
769
- <div style="width:100%;height:100%;display:flex;justify-content:${flexJustify};align-items:${flexAlign};padding:8px;box-sizing:border-box;">
770
- <div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">
771
- ${text || ""}
772
- </div>
773
- </div>
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>
774
776
  </div>`;
775
777
  }
776
778
  // Outlook (VML) fallback
@@ -793,16 +795,16 @@ async function convertShapeBlock(blockData) {
793
795
  msoBakeImageWithText,
794
796
  }, visibilityClass);
795
797
  // Combine into table wrapper
796
- return `
797
- <table width="100%" style="border-collapse:collapse;table-layout:fixed;" class="${visibilityClass}">
798
- <tr>
799
- <td style="padding:${padding.top || 0}px ${padding.right || 0}px ${padding.bottom || 0}px ${padding.left || 0}px;text-align:${alignment};">
800
- ${outlookContent}
801
- <!--[if !mso]><!-->
802
- ${nonMsoContent}
803
- <!--<![endif]-->
804
- </td>
805
- </tr>
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>
806
808
  </table>`;
807
809
  }
808
810
  // ---------- Updated VML builder ----------
@@ -835,24 +837,24 @@ function buildVMLShape({ shape, widthPx, heightPx, imageUrl, backgroundColor, bo
835
837
  const safeFontSize = Math.max(Math.round(textSize), 10);
836
838
  // Build the textbox with table/cell for reliable vertical centering in Outlook
837
839
  const textboxMarkup = text && !msoHasBakedText
838
- ? `<v:textbox inset="6pt,6pt,6pt,6pt" style="mso-fit-shape-to-text:false;">
839
- <div style="display:table;width:100%;height:100%;">
840
- <div style="display:table-cell;vertical-align:${vAlign};text-align:${hAlign};padding:0 6px;">
841
- <div style="color:${textColor};font-family:Arial, sans-serif;font-size:${safeFontSize}px;line-height:1.3;word-wrap:break-word;">
842
- ${text}
843
- </div>
844
- </div>
845
- </div>
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>
846
848
  </v:textbox>`
847
849
  : `<v:textbox inset="0,0,0,0"><div style="display:none;">.</div></v:textbox>`;
848
850
  // Return VML shape
849
- return `
850
- <v:${tag} xmlns:v="urn:schemas-microsoft-com:vml"
851
- style="width:${widthPx}px;height:${heightPx}px;display:inline-block;"
852
- ${borderAttrs}
853
- fill="true" fillcolor="${fillColor}"${extraAttr}>
854
- ${fillMarkup}
855
- ${textboxMarkup}
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}
856
858
  </v:${tag}>`;
857
859
  }
858
860
  function appendOutlookForShape(content, outerContainerWidth, innerContainerWidth, opts, visibilityClass) {
@@ -880,28 +882,28 @@ function appendOutlookForShape(content, outerContainerWidth, innerContainerWidth
880
882
  const shouldHideInOutlook = visibilityClass.includes("hide-desktop");
881
883
  // Fix: Properly handle Outlook visibility with conditional comments
882
884
  if (shouldHideInOutlook) {
883
- return `<!--[if !mso]><!-->
884
- <table align="${align}" border="0" cellpadding="0" cellspacing="0"
885
- style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
886
- <tr>
887
- <td valign="${valign}"
888
- style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
889
- ${vml}
890
- </td>
891
- </tr>
892
- </table>
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>
893
895
  <!--<![endif]-->`;
894
896
  }
895
- return `<!--[if mso]>
896
- <table align="${align}" border="0" cellpadding="0" cellspacing="0"
897
- style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
898
- <tr>
899
- <td valign="${valign}"
900
- style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
901
- ${vml}
902
- </td>
903
- </tr>
904
- </table>
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>
905
907
  <![endif]-->`;
906
908
  }
907
909
  function convertVerticalDividerBlockToHtml(blockData) {
@@ -914,19 +916,19 @@ function convertVerticalDividerBlockToHtml(blockData) {
914
916
  pxChanges: allPxAttributes,
915
917
  });
916
918
  // Outlook-safe vertical divider
917
- const dividerContent = `
918
- <table cellpadding="0" cellspacing="0" border="0" align="center" style="width:auto; ${convertedStyle}">
919
- <tr>
920
- <td style="vertical-align: middle; text-align: center;">
921
- <!--[if mso | IE]>
922
- <v:rect xmlns:v="urn:schemas-microsoft-com:vml" fillcolor="${dividerColor}" style="width:${width}px;height:${height}px;" stroke="f"></v:rect>
923
- <![endif]-->
924
- <!--[if !mso]><!-- -->
925
- <div style="display:inline-block;width:${width}px;height:${height}px;background:${dividerColor};line-height:0;font-size:0;">&nbsp;</div>
926
- <!--<![endif]-->
927
- </td>
928
- </tr>
929
- </table>
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;">&nbsp;</div>
928
+ <!--<![endif]-->
929
+ </td>
930
+ </tr>
931
+ </table>
930
932
  `;
931
933
  return appendOutlookSupport(dividerContent, convertedStyle, visibilityClass);
932
934
  }
package/package.json CHANGED
@@ -1,33 +1,33 @@
1
- {
2
- "name": "email-builder-utils",
3
- "version": "1.1.34",
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.35",
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
+ }