email-builder-utils 1.1.52 → 1.1.53

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":"button.d.ts","sourceRoot":"","sources":["../../../src/utils/blocks/button.ts"],"names":[],"mappings":"AA6IA,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,GAAG,UAuFhD"}
1
+ {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../src/utils/blocks/button.ts"],"names":[],"mappings":"AA2IA,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,GAAG,UAuFhD"}
@@ -55,15 +55,15 @@ function appendOutlookForButton(buttonData) {
55
55
  const tableMargin = containerAlign === 'center' ? 'margin:0 auto;'
56
56
  : containerAlign === 'right' ? 'margin-left:auto;margin-right:0;'
57
57
  : '';
58
- const nonMsoAnchor = `<!--[if !mso]><!-->
59
- <table border="0" cellpadding="0" cellspacing="0" role="presentation"${tableAlignAttr} style="border-collapse:separate;${tableMargin}">
60
- <tr>
61
- <td bgcolor="${bgColor}" align="center" valign="middle"${tdWidthAttr}${tdHeightAttr} style="background-color:${bgColor};border-radius:${br}px;${borderCss}${tdWidthCss}${tdHeightCss}box-sizing:border-box;mso-padding-alt:0;text-align:center;">
62
- <a href="${safeHref}" target="_blank" rel="noreferrer noopener"
63
- style="${anchorBoxStyles}color:${safeColor};font-family:${safeFF};font-size:${fs}px;font-weight:${fontWeight};text-decoration:none;text-align:center;white-space:nowrap;-webkit-text-size-adjust:none;box-sizing:border-box;">${text}</a>
64
- </td>
65
- </tr>
66
- </table>
58
+ const nonMsoAnchor = `<!--[if !mso]><!-->
59
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation"${tableAlignAttr} style="border-collapse:separate;${tableMargin}">
60
+ <tr>
61
+ <td bgcolor="${bgColor}" align="center" valign="middle"${tdWidthAttr}${tdHeightAttr} style="background-color:${bgColor};border-radius:${br}px;${borderCss}${tdWidthCss}${tdHeightCss}box-sizing:border-box;mso-padding-alt:0;text-align:center;">
62
+ <a href="${safeHref}" target="_blank" rel="noreferrer noopener"
63
+ style="${anchorBoxStyles}color:${safeColor};font-family:${safeFF};font-size:${fs}px;font-weight:${fontWeight};text-decoration:none;text-align:center;white-space:nowrap;-webkit-text-size-adjust:none;box-sizing:border-box;">${text}</a>
64
+ </td>
65
+ </tr>
66
+ </table>
67
67
  <!--<![endif]-->`;
68
68
  // ── MSO: VML bulletproof button (classic Outlook / Word engine) ──
69
69
  // VML arcsize is a percentage of half the shorter side. Clamp to 50% (pill).
@@ -71,18 +71,16 @@ function appendOutlookForButton(buttonData) {
71
71
  const strokeAttrs = bw > 0
72
72
  ? `stroke="true" strokecolor="${bdColor}" strokeweight="${bw}px"`
73
73
  : `stroke="false"`;
74
- const msoButton = `<!--[if mso]>
75
- <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word"
76
- href="${safeHref}"
77
- style="height:${finalHeight}px;v-text-anchor:middle;width:${vmlWidth}px;"
78
- arcsize="${arcSizePct}%"
79
- ${strokeAttrs}
80
- fillcolor="${bgColor}">
81
- <w:anchorlock/>
82
- <v:textbox inset="0,0,0,0">
83
- <center style="color:${safeColor};font-family:${safeFF};font-size:${fs}px;font-weight:${fontWeight};mso-line-height-rule:exactly;line-height:${finalHeight}px;text-decoration:none;">${text}</center>
84
- </v:textbox>
85
- </v:roundrect>
74
+ const msoButton = `<!--[if mso]>
75
+ <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word"
76
+ href="${safeHref}"
77
+ style="height:${finalHeight}px;v-text-anchor:middle;width:${vmlWidth}px;"
78
+ arcsize="${arcSizePct}%"
79
+ ${strokeAttrs}
80
+ fillcolor="${bgColor}">
81
+ <w:anchorlock/>
82
+ <center style="color:${safeColor};font-family:${safeFF};font-size:${fs}px;font-weight:${fontWeight};mso-line-height-rule:exactly;line-height:${fs}px;text-decoration:none;">${text}</center>
83
+ </v:roundrect>
86
84
  <![endif]-->`;
87
85
  const innerContent = containerAlign === "center"
88
86
  ? `<center>${msoButton}${nonMsoAnchor}</center>`
@@ -167,14 +165,14 @@ function convertButtonBlock(blockData) {
167
165
  hideOnDesktop: Boolean(props.hideOnDesktop),
168
166
  hideOnMobile: Boolean(props.hideOnMobile),
169
167
  });
170
- return `
171
- <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" class="${visibilityClass}" data-block-type="button" data-block-props="${buttonBlockProps}" style="border-collapse:collapse;table-layout:fixed;">
172
- <tr>
173
- <td align="${computed.containerAlign}"${(0, common_1.buildOutlookBgAttr)(containerBg)}
174
- style="padding:${padding?.top || 0}px ${padding?.right || 0}px ${padding?.bottom || 0}px ${padding?.left || 0}px;background-color:${containerBg || 'transparent'};">
175
- ${innerContent}
176
- </td>
177
- </tr>
178
- </table>
168
+ return `
169
+ <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" class="${visibilityClass}" data-block-type="button" data-block-props="${buttonBlockProps}" style="border-collapse:collapse;table-layout:fixed;">
170
+ <tr>
171
+ <td align="${computed.containerAlign}"${(0, common_1.buildOutlookBgAttr)(containerBg)}
172
+ style="padding:${padding?.top || 0}px ${padding?.right || 0}px ${padding?.bottom || 0}px ${padding?.left || 0}px;background-color:${containerBg || 'transparent'};">
173
+ ${innerContent}
174
+ </td>
175
+ </tr>
176
+ </table>
179
177
  `;
180
178
  }
@@ -21,23 +21,23 @@ function convertDividerBlockToHtml(blockData) {
21
21
  hideOnMobile ? "hide-mobile" : "",
22
22
  hideOnDesktop ? "hide-desktop" : "",
23
23
  ].filter(Boolean).join(" ");
24
- const dividerContent = `
25
- <table
26
- align="${alignAttr}"
27
- width="${dividerWidth}%"
28
- cellpadding="0"
29
- cellspacing="0"
30
- style="margin:${alignMargin};"
31
- >
32
- <tr>
33
- <td
34
- height="${thickness}"
35
- style="font-size:1px; line-height:1px; background:${dividerColor}; width:100%;"
36
- >
37
- &nbsp;
38
- </td>
39
- </tr>
40
- </table>
24
+ const dividerContent = `
25
+ <table
26
+ align="${alignAttr}"
27
+ width="${dividerWidth}%"
28
+ cellpadding="0"
29
+ cellspacing="0"
30
+ style="margin:${alignMargin};"
31
+ >
32
+ <tr>
33
+ <td
34
+ height="${thickness}"
35
+ style="font-size:1px; line-height:1px; background:${dividerColor}; width:100%;"
36
+ >
37
+ &nbsp;
38
+ </td>
39
+ </tr>
40
+ </table>
41
41
  `;
42
42
  return (0, outlookSupport_1.appendOutlookSupport)(dividerContent, contentStyle, visibilityClass);
43
43
  }
@@ -55,18 +55,18 @@ function convertVerticalDividerBlockToHtml(blockData) {
55
55
  perChanges: [],
56
56
  pxChanges: buildStyles_1.allPxAttributes,
57
57
  });
58
- return `
59
- <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
60
- style="${buildStyles_1.tableCommonStyle}; max-width:600px;" class="${visibilityClass}" data-block-type="vdivider">
61
- <tr>
62
- <td style="${outerStyles}; text-align:center; vertical-align:middle;">
63
- <!--[if mso | IE]>
64
- <v:rect xmlns:v="urn:schemas-microsoft-com:vml" fillcolor="${dividerColor}" style="width:${width}px;height:${height}px;" stroke="f"></v:rect>
65
- <![endif]-->
66
- <!--[if !mso]><!-->
67
- <div style="display:inline-block;width:${width}px;height:${height}px;background:${dividerColor};line-height:0;font-size:0;">&nbsp;</div>
68
- <!--<![endif]-->
69
- </td>
70
- </tr>
58
+ return `
59
+ <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
60
+ style="${buildStyles_1.tableCommonStyle}; max-width:600px;" class="${visibilityClass}" data-block-type="vdivider">
61
+ <tr>
62
+ <td style="${outerStyles}; text-align:center; vertical-align:middle;">
63
+ <!--[if mso | IE]>
64
+ <v:rect xmlns:v="urn:schemas-microsoft-com:vml" fillcolor="${dividerColor}" style="width:${width}px;height:${height}px;" stroke="f"></v:rect>
65
+ <![endif]-->
66
+ <!--[if !mso]><!-->
67
+ <div style="display:inline-block;width:${width}px;height:${height}px;background:${dividerColor};line-height:0;font-size:0;">&nbsp;</div>
68
+ <!--<![endif]-->
69
+ </td>
70
+ </tr>
71
71
  </table>`;
72
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../src/utils/blocks/grid.ts"],"names":[],"mappings":"AAMA,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,GAAG,EACd,QAAQ,EAAE,GAAG,EACb,aAAa,EAAE,MAAM,mBA0TtB;AAED,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,GAAG,EACd,QAAQ,EAAE,GAAG,EACb,gBAAgB,EAAE,MAAM,EACxB,iBAAiB,EAAE,MAAM,EACzB,mBAAmB,UAAQ;;;GAwG5B"}
1
+ {"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../src/utils/blocks/grid.ts"],"names":[],"mappings":"AAMA,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,GAAG,EACd,QAAQ,EAAE,GAAG,EACb,aAAa,EAAE,MAAM,mBA0TtB;AAED,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,GAAG,EACd,QAAQ,EAAE,GAAG,EACb,gBAAgB,EAAE,MAAM,EACxB,iBAAiB,EAAE,MAAM,EACzB,mBAAmB,UAAQ;;;GA0G5B"}
@@ -91,18 +91,18 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
91
91
  : '';
92
92
  const divWrapOpen = divBorderStyle ? `<div style="${divBorderStyle}${divWrapBg}">` : '';
93
93
  const divWrapClose = divBorderStyle ? `</div>` : '';
94
- let html = `
95
- <!--[if mso]>
96
- <table border="0" cellpadding="0" cellspacing="0" width="${msoTableWidth}"${msoBgAttr}
97
- style="border-collapse:collapse;width:${msoTableWidth}px;${msoBgStyle}${innerBgTransparent}"
98
- class="${gridVisibilityClass}">
99
- <![endif]-->
100
- <!--[if !mso]><!-->
101
- <table border="0" cellpadding="0" cellspacing="0" width="100%" align="center"
102
- role="presentation"${nonMsoBgAttr}
103
- style="border-collapse:collapse; table-layout:fixed; ${innerBgTransparent}${tableStyles}; max-width:600px;"
104
- class="${gridVisibilityClass}">
105
- <!--<![endif]-->
94
+ let html = `
95
+ <!--[if mso]>
96
+ <table border="0" cellpadding="0" cellspacing="0" width="${msoTableWidth}"${msoBgAttr}
97
+ style="border-collapse:collapse;width:${msoTableWidth}px;${msoBgStyle}${innerBgTransparent}"
98
+ class="${gridVisibilityClass}">
99
+ <![endif]-->
100
+ <!--[if !mso]><!-->
101
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" align="center"
102
+ role="presentation"${nonMsoBgAttr}
103
+ style="border-collapse:collapse; table-layout:fixed; ${innerBgTransparent}${tableStyles}; max-width:600px;"
104
+ class="${gridVisibilityClass}">
105
+ <!--<![endif]-->
106
106
  `;
107
107
  for (let r = 0; r < visualRows; r++) {
108
108
  html += "<tr>";
@@ -167,13 +167,13 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
167
167
  const canApplyGridBgFallback = !cellHasBorderRadius && !divBorderStyle;
168
168
  const effectiveCellBg = cellBgColor || (canApplyGridBgFallback ? msoBgColor : '');
169
169
  const cellBgAttr = (0, common_1.buildOutlookBgAttr)(effectiveCellBg);
170
- html += `
171
- <td
172
- width="${Math.max(1, Math.round(widthPercent))}%"${cellBgAttr}
173
- class="${[responsive ? "stack-column" : "", visibilityClass].filter(Boolean).join(" ")}"
174
- style="width:${widthPercent}%;vertical-align:${verticalAlign};word-break:break-word;${styles}"
175
- >
176
- ${childHtml}
170
+ html += `
171
+ <td
172
+ width="${Math.max(1, Math.round(widthPercent))}%"${cellBgAttr}
173
+ class="${[responsive ? "stack-column" : "", visibilityClass].filter(Boolean).join(" ")}"
174
+ style="width:${widthPercent}%;vertical-align:${verticalAlign};word-break:break-word;${styles}"
175
+ >
176
+ ${childHtml}
177
177
  </td>`;
178
178
  // Spacer td between columns — uses width attribute only (no inline style width) so
179
179
  // Outlook mobile (which strips <style>) treats it proportionally, not as a fixed blocker.
@@ -186,11 +186,11 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
186
186
  }
187
187
  else {
188
188
  const emptyBgAttr = divBorderStyle ? '' : (0, common_1.buildOutlookBgAttr)(msoBgColor);
189
- html += `
190
- <td width="${Math.max(1, Math.round(widthPercent))}%"
191
- ${emptyBgAttr}
192
- ${responsive ? 'class="stack-column"' : ""}
193
- style="width:${widthPercent}%;vertical-align:top;">
189
+ html += `
190
+ <td width="${Math.max(1, Math.round(widthPercent))}%"
191
+ ${emptyBgAttr}
192
+ ${responsive ? 'class="stack-column"' : ""}
193
+ style="width:${widthPercent}%;vertical-align:top;">
194
194
  </td>`;
195
195
  if (columnGap > 0 && c !== lastVisibleCol) {
196
196
  const gapBgAttr = divBorderStyle ? '' : (0, common_1.buildOutlookBgAttr)(msoBgColor);
@@ -201,13 +201,13 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
201
201
  html += "</tr>";
202
202
  }
203
203
  // Close both MSO and non-MSO tables
204
- html += `
205
- <!--[if mso]>
206
- </table>
207
- <![endif]-->
208
- <!--[if !mso]><!-->
209
- </table>
210
- <!--<![endif]-->
204
+ html += `
205
+ <!--[if mso]>
206
+ </table>
207
+ <![endif]-->
208
+ <!--[if !mso]><!-->
209
+ </table>
210
+ <!--<![endif]-->
211
211
  `;
212
212
  // ── Background image: canonical multi-client approach ────────────────────
213
213
  //
@@ -233,35 +233,35 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
233
233
  return `<v:fill type="gradient" color="${c1}" color2="${c2}" angle="${vmlAngle}" />`;
234
234
  })()
235
235
  : `<v:fill type="frame" src="${rawBgImageUrl}" color="${fallbackBgColor}" />`;
236
- html = `
237
- <table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation"
238
- style="border-collapse:collapse;table-layout:fixed;width:100%;max-width:${msoTableWidth}px;">
239
- <tr>
240
- <td width="100%"${(0, common_1.buildOutlookBgAttr)(fallbackBgColor)} valign="top"
241
- ${!isGradient && rawBgImageUrl ? `background="${rawBgImageUrl}"` : ""}
242
- style="
243
- width:100%;max-width:${msoTableWidth}px;
244
- background-color:${fallbackBgColor};
245
- ${isGradient ? `background:${effectiveGradient};` : `background-image:url('${rawBgImageUrl}'); background-position:center center; background-size:cover; background-repeat:no-repeat;`}
246
- ">
247
-
248
- <!--[if gte mso 9]>
249
- <v:rect xmlns:v="urn:schemas-microsoft-com:vml"
250
- fill="true" stroke="false"
251
- style="width:${msoTableWidth}px;">
252
- ${vmlFill}
253
- <v:textbox inset="0,0,0,0">
254
- <![endif]-->
255
-
256
- ${html}
257
-
258
- <!--[if gte mso 9]>
259
- </v:textbox>
260
- </v:rect>
261
- <![endif]-->
262
-
263
- </td>
264
- </tr>
236
+ html = `
237
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation"
238
+ style="border-collapse:collapse;table-layout:fixed;width:100%;max-width:${msoTableWidth}px;">
239
+ <tr>
240
+ <td width="100%"${(0, common_1.buildOutlookBgAttr)(fallbackBgColor)} valign="top"
241
+ ${!isGradient && rawBgImageUrl ? `background="${rawBgImageUrl}"` : ""}
242
+ style="
243
+ width:100%;max-width:${msoTableWidth}px;
244
+ background-color:${fallbackBgColor};
245
+ ${isGradient ? `background:${effectiveGradient};` : `background-image:url('${rawBgImageUrl}'); background-position:center center; background-size:cover; background-repeat:no-repeat;`}
246
+ ">
247
+
248
+ <!--[if gte mso 9]>
249
+ <v:rect xmlns:v="urn:schemas-microsoft-com:vml"
250
+ fill="true" stroke="false"
251
+ style="width:${msoTableWidth}px;">
252
+ ${vmlFill}
253
+ <v:textbox inset="0,0,0,0">
254
+ <![endif]-->
255
+
256
+ ${html}
257
+
258
+ <!--[if gte mso 9]>
259
+ </v:textbox>
260
+ </v:rect>
261
+ <![endif]-->
262
+
263
+ </td>
264
+ </tr>
265
265
  </table>`;
266
266
  }
267
267
  // Wrap the entire grid (including any bg-image outer table) in a div when the block
@@ -320,7 +320,7 @@ async function convertGridCellBlock(blockData, rootData, cellWidthPercent, paren
320
320
  }
321
321
  }
322
322
  const borderRadius = cellBorderRadius || 0;
323
- const bgColor = styleWithoutBorder?.backgroundColor || "transparent";
323
+ const bgColor = styleWithoutBorder?.backgroundColor || "";
324
324
  // Build border CSS for the div wrapper.
325
325
  // When the parent grid already has a divBorderStyle wrapper (border + border-radius +
326
326
  // overflow:hidden), the cell must NOT duplicate the same border/radius — that causes
@@ -342,8 +342,10 @@ async function convertGridCellBlock(blockData, rootData, cellWidthPercent, paren
342
342
  }
343
343
  const cellDivBorderStyle = cellDivBorderParts.join(' ');
344
344
  // Unconditional div — visible to all clients (Gmail, Outlook new/old, Apple Mail).
345
- // background-color on the div covers modern clients; bgcolor on <td> covers Old Outlook.
346
- const divStyleParts = [`background-color:${bgColor};`];
345
+ // background-color on the div covers modern clients when a real color is set.
346
+ // When no color is set, omit the property entirely — writing background-color:transparent
347
+ // defeats the bgcolor attribute on the parent <td> in Old Outlook's Word engine.
348
+ const divStyleParts = bgColor ? [`background-color:${bgColor};`] : [];
347
349
  const formatPad = (value) => (typeof value === 'number' ? `${value}px` : (value || '0'));
348
350
  const cellPadTop = formatPad(cellPad.top);
349
351
  const cellPadBottom = formatPad(cellPad.bottom);
@@ -33,23 +33,23 @@ function buildVMLShape({ shape, widthPx, heightPx, imageUrl, backgroundColor, bo
33
33
  const hAlign = hAlignMap[textAlign] || "center";
34
34
  const safeFontSize = Math.max(Math.round(textSize), 10);
35
35
  const textboxMarkup = text && !msoHasBakedText
36
- ? `<v:textbox inset="6pt,6pt,6pt,6pt" style="mso-fit-shape-to-text:false;">
37
- <div style="display:table;width:100%;height:100%;">
38
- <div style="display:table-cell;vertical-align:${vAlign};text-align:${hAlign};padding:0 6px;">
39
- <div style="color:${textColor};font-family:Arial, sans-serif;font-size:${safeFontSize}px;line-height:1.3;word-wrap:break-word;">
40
- ${text}
41
- </div>
42
- </div>
43
- </div>
36
+ ? `<v:textbox inset="6pt,6pt,6pt,6pt" style="mso-fit-shape-to-text:false;">
37
+ <div style="display:table;width:100%;height:100%;">
38
+ <div style="display:table-cell;vertical-align:${vAlign};text-align:${hAlign};padding:0 6px;">
39
+ <div style="color:${textColor};font-family:Arial, sans-serif;font-size:${safeFontSize}px;line-height:1.3;word-wrap:break-word;">
40
+ ${text}
41
+ </div>
42
+ </div>
43
+ </div>
44
44
  </v:textbox>`
45
45
  : `<v:textbox inset="0,0,0,0"><div style="display:none;">.</div></v:textbox>`;
46
- return `
47
- <v:${tag} xmlns:v="urn:schemas-microsoft-com:vml"
48
- style="width:${widthPx}px;height:${heightPx}px;display:inline-block;"
49
- ${borderAttrs}
50
- fill="true" fillcolor="${fillColor}"${extraAttr}>
51
- ${fillMarkup}
52
- ${textboxMarkup}
46
+ return `
47
+ <v:${tag} xmlns:v="urn:schemas-microsoft-com:vml"
48
+ style="width:${widthPx}px;height:${heightPx}px;display:inline-block;"
49
+ ${borderAttrs}
50
+ fill="true" fillcolor="${fillColor}"${extraAttr}>
51
+ ${fillMarkup}
52
+ ${textboxMarkup}
53
53
  </v:${tag}>`;
54
54
  }
55
55
  function appendOutlookForShape(content, outerContainerWidth, innerContainerWidth, opts, visibilityClass) {
@@ -70,28 +70,28 @@ function appendOutlookForShape(content, outerContainerWidth, innerContainerWidth
70
70
  const valign = opts.verticalAlign || "middle";
71
71
  const shouldHideInOutlook = visibilityClass.includes("hide-desktop");
72
72
  if (shouldHideInOutlook) {
73
- return `<!--[if !mso]><!-->
74
- <table align="${align}" border="0" cellpadding="0" cellspacing="0"
75
- style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
76
- <tr>
77
- <td valign="${valign}"
78
- style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
79
- ${vml}
80
- </td>
81
- </tr>
82
- </table>
73
+ return `<!--[if !mso]><!-->
74
+ <table align="${align}" border="0" cellpadding="0" cellspacing="0"
75
+ style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
76
+ <tr>
77
+ <td valign="${valign}"
78
+ style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
79
+ ${vml}
80
+ </td>
81
+ </tr>
82
+ </table>
83
83
  <!--<![endif]-->`;
84
84
  }
85
- return `<!--[if mso]>
86
- <table align="${align}" border="0" cellpadding="0" cellspacing="0"
87
- style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
88
- <tr>
89
- <td valign="${valign}"
90
- style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
91
- ${vml}
92
- </td>
93
- </tr>
94
- </table>
85
+ return `<!--[if mso]>
86
+ <table align="${align}" border="0" cellpadding="0" cellspacing="0"
87
+ style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
88
+ <tr>
89
+ <td valign="${valign}"
90
+ style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
91
+ ${vml}
92
+ </td>
93
+ </tr>
94
+ </table>
95
95
  <![endif]-->`;
96
96
  }
97
97
  async function convertShapeBlock(blockData) {
@@ -146,58 +146,58 @@ async function convertShapeBlock(blockData) {
146
146
  let nonMsoContent = "";
147
147
  // --- Case 1: Image + Text ---
148
148
  if (imageUrl && text) {
149
- nonMsoContent = `
150
- <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
151
- border:${borderWidth}px ${borderStyle} ${borderColor};
152
- border-radius:${resolvedBorderRadius};
153
- background-color:${finalBackgroundColor};
154
- background-image:url('${imageUrl}');
155
- background-position:center center;
156
- background-size:cover;
157
- background-repeat:no-repeat;
158
- overflow:hidden;${alignmentStyle}${customCss || ""}">
159
- <table border="0" cellpadding="0" cellspacing="0" width="100%"
160
- style="width:100%;height:${resolvedHeightPx}px;border-collapse:collapse;">
161
- <tr>
162
- <td align="${textAlignStyle}" valign="${verticalAlign}"
163
- height="${resolvedHeightPx}"
164
- style="padding:6px;vertical-align:${verticalAlign};text-align:${textAlignStyle};overflow:hidden;box-sizing:border-box;">
165
- <div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">${text}</div>
166
- </td>
167
- </tr>
168
- </table>
149
+ nonMsoContent = `
150
+ <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
151
+ border:${borderWidth}px ${borderStyle} ${borderColor};
152
+ border-radius:${resolvedBorderRadius};
153
+ background-color:${finalBackgroundColor};
154
+ background-image:url('${imageUrl}');
155
+ background-position:center center;
156
+ background-size:cover;
157
+ background-repeat:no-repeat;
158
+ overflow:hidden;${alignmentStyle}${customCss || ""}">
159
+ <table border="0" cellpadding="0" cellspacing="0" width="100%"
160
+ style="width:100%;height:${resolvedHeightPx}px;border-collapse:collapse;">
161
+ <tr>
162
+ <td align="${textAlignStyle}" valign="${verticalAlign}"
163
+ height="${resolvedHeightPx}"
164
+ style="padding:6px;vertical-align:${verticalAlign};text-align:${textAlignStyle};overflow:hidden;box-sizing:border-box;">
165
+ <div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">${text}</div>
166
+ </td>
167
+ </tr>
168
+ </table>
169
169
  </div>`;
170
170
  }
171
171
  // --- Case 2: Image only ---
172
172
  else if (imageUrl) {
173
- nonMsoContent = `
174
- <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
175
- border:${borderWidth}px ${borderStyle} ${borderColor};
176
- border-radius:${resolvedBorderRadius};
177
- overflow:hidden;${alignmentStyle}${customCss || ""}">
178
- <img src="${imageUrl}" alt="${text || "shape image"}"
179
- width="${resolvedWidthPx}" height="${resolvedHeightPx}"
180
- style="width:100%;height:100%;object-fit:cover;border-radius:${resolvedBorderRadius};display:block;" />
173
+ nonMsoContent = `
174
+ <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
175
+ border:${borderWidth}px ${borderStyle} ${borderColor};
176
+ border-radius:${resolvedBorderRadius};
177
+ overflow:hidden;${alignmentStyle}${customCss || ""}">
178
+ <img src="${imageUrl}" alt="${text || "shape image"}"
179
+ width="${resolvedWidthPx}" height="${resolvedHeightPx}"
180
+ style="width:100%;height:100%;object-fit:cover;border-radius:${resolvedBorderRadius};display:block;" />
181
181
  </div>`;
182
182
  }
183
183
  // --- Case 3: Text only ---
184
184
  else {
185
- nonMsoContent = `
186
- <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
187
- background-color:${finalBackgroundColor};
188
- border:${borderWidth}px ${borderStyle} ${borderColor};
189
- border-radius:${resolvedBorderRadius};
190
- overflow:hidden;${alignmentStyle}${customCss || ""}">
191
- <table border="0" cellpadding="0" cellspacing="0" width="100%"
192
- style="width:100%;height:${resolvedHeightPx}px;border-collapse:collapse;">
193
- <tr>
194
- <td align="${textAlignStyle}" valign="${verticalAlign}"
195
- height="${resolvedHeightPx}"
196
- style="padding:8px;vertical-align:${verticalAlign};text-align:${textAlignStyle};box-sizing:border-box;">
197
- <div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">${text || ""}</div>
198
- </td>
199
- </tr>
200
- </table>
185
+ nonMsoContent = `
186
+ <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
187
+ background-color:${finalBackgroundColor};
188
+ border:${borderWidth}px ${borderStyle} ${borderColor};
189
+ border-radius:${resolvedBorderRadius};
190
+ overflow:hidden;${alignmentStyle}${customCss || ""}">
191
+ <table border="0" cellpadding="0" cellspacing="0" width="100%"
192
+ style="width:100%;height:${resolvedHeightPx}px;border-collapse:collapse;">
193
+ <tr>
194
+ <td align="${textAlignStyle}" valign="${verticalAlign}"
195
+ height="${resolvedHeightPx}"
196
+ style="padding:8px;vertical-align:${verticalAlign};text-align:${textAlignStyle};box-sizing:border-box;">
197
+ <div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">${text || ""}</div>
198
+ </td>
199
+ </tr>
200
+ </table>
201
201
  </div>`;
202
202
  }
203
203
  // Outlook (VML) fallback
@@ -242,15 +242,15 @@ async function convertShapeBlock(blockData) {
242
242
  hideOnMobile: Boolean(props.hideOnMobile),
243
243
  });
244
244
  // Combine into table wrapper
245
- return `
246
- <table width="100%" style="border-collapse:collapse;table-layout:fixed;max-width:600px;" class="${visibilityClass}" data-block-type="shape" data-block-props="${shapeBlockProps}">
247
- <tr>
248
- <td style="padding:${padding.top || 0}px ${padding.right || 0}px ${padding.bottom || 0}px ${padding.left || 0}px;text-align:${alignment};">
249
- ${outlookContent}
250
- <!--[if !mso]><!-->
251
- ${nonMsoContent}
252
- <!--<![endif]-->
253
- </td>
254
- </tr>
245
+ return `
246
+ <table width="100%" style="border-collapse:collapse;table-layout:fixed;max-width:600px;" class="${visibilityClass}" data-block-type="shape" data-block-props="${shapeBlockProps}">
247
+ <tr>
248
+ <td style="padding:${padding.top || 0}px ${padding.right || 0}px ${padding.bottom || 0}px ${padding.left || 0}px;text-align:${alignment};">
249
+ ${outlookContent}
250
+ <!--[if !mso]><!-->
251
+ ${nonMsoContent}
252
+ <!--<![endif]-->
253
+ </td>
254
+ </tr>
255
255
  </table>`;
256
256
  }
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/utils/blocks/text.ts"],"names":[],"mappings":"AASA,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,CAAC,EAAE,MAAM,UAkLtE"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/utils/blocks/text.ts"],"names":[],"mappings":"AASA,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,CAAC,EAAE,MAAM,UAqLtE"}
@@ -101,34 +101,37 @@ function convertTextBlock(blockData, cellWidthInPx) {
101
101
  return `<v:fill type="gradient" color="${c1}" color2="${c2}" angle="${vmlAngle}" />`;
102
102
  })()
103
103
  : `<v:fill type="frame" src="${rawBgImageUrl}" color="${fallbackBgColor}" />`;
104
+ const bgPosition = rest.backgroundPosition || 'center center';
105
+ const bgSize = rest.backgroundSize || 'cover';
106
+ const bgRepeat = rest.backgroundRepeat || 'no-repeat';
104
107
  const bgCss = isGradient
105
108
  ? `background:${effectiveGradient};`
106
- : `background-image:url('${rawBgImageUrl}'); background-position:center center; background-size:cover; background-repeat:no-repeat;`;
107
- const wrappedContent = `
108
- <table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation"
109
- style="border-collapse:collapse;width:100%;max-width:${msoWidth}px;" class="${visibilityClass}">
110
- <tr>
111
- <td width="100%"${(0, common_1.buildOutlookBgAttr)(fallbackBgColor)} valign="top"
112
- ${!isGradient && rawBgImageUrl ? `background="${rawBgImageUrl}"` : ""}
113
- style="width:100%;max-width:${msoWidth}px;background-color:${fallbackBgColor};${bgCss}">
114
-
115
- <!--[if gte mso 9]>
116
- <v:rect xmlns:v="urn:schemas-microsoft-com:vml"
117
- fill="true" stroke="false"
118
- style="width:${msoWidth}px;">
119
- ${vmlFill}
120
- <v:textbox inset="0,0,0,0">
121
- <![endif]-->
122
-
123
- ${textContent}
124
-
125
- <!--[if gte mso 9]>
126
- </v:textbox>
127
- </v:rect>
128
- <![endif]-->
129
-
130
- </td>
131
- </tr>
109
+ : `background-image:url('${rawBgImageUrl}'); background-position:${bgPosition}; background-size:${bgSize}; background-repeat:${bgRepeat};`;
110
+ const wrappedContent = `
111
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation"
112
+ style="border-collapse:collapse;width:100%;max-width:${msoWidth}px;" class="${visibilityClass}">
113
+ <tr>
114
+ <td width="100%"${(0, common_1.buildOutlookBgAttr)(fallbackBgColor)} valign="top"
115
+ ${!isGradient && rawBgImageUrl ? `background="${rawBgImageUrl}"` : ""}
116
+ style="width:100%;max-width:${msoWidth}px;background-color:${fallbackBgColor};${bgCss}">
117
+
118
+ <!--[if gte mso 9]>
119
+ <v:rect xmlns:v="urn:schemas-microsoft-com:vml"
120
+ fill="true" stroke="false"
121
+ style="width:${msoWidth}px;">
122
+ ${vmlFill}
123
+ <v:textbox inset="0,0,0,0">
124
+ <![endif]-->
125
+
126
+ ${textContent}
127
+
128
+ <!--[if gte mso 9]>
129
+ </v:textbox>
130
+ </v:rect>
131
+ <![endif]-->
132
+
133
+ </td>
134
+ </tr>
132
135
  </table>`;
133
136
  return navigateToUrl
134
137
  ? `<a href="${navigateToUrl}" rel="noreferrer noopener" style="${linkColorStyle}text-decoration:none;cursor:pointer;">${wrappedContent}</a>`