email-builder-utils 1.1.50 → 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"}
@@ -79,9 +79,7 @@ function appendOutlookForButton(buttonData) {
79
79
  ${strokeAttrs}
80
80
  fillcolor="${bgColor}">
81
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>
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>
85
83
  </v:roundrect>
86
84
  <![endif]-->`;
87
85
  const innerContent = containerAlign === "center"
@@ -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,mBAgTtB;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"}
@@ -157,10 +157,16 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
157
157
  const visibilityClass = (0, common_1.getVisibilityClass)(childProps);
158
158
  if (childVisible) {
159
159
  const { html: childHtml, styles } = await convertGridCellBlock(child, rootData, widthPercent, adjustedTableWidth, Boolean(divBorderStyle));
160
- // bgcolor on the cell <td> ensures background-color survives Outlook
161
- // compose paste (Word/Web editors strip CSS but keep bgcolor attribute).
160
+ // bgcolor on the cell <td> for Old Outlook (Word engine).
161
+ // When the cell has no border-radius and the grid has no border, use the grid's
162
+ // backgroundColor as the fallback so the grid background shows through empty cells.
163
+ // Skipped for border-radius cells — bgcolor is rectangular and would bleed through
164
+ // the div's rounded-corner clip in CSS-capable clients (corner squares).
162
165
  const cellBgColor = cellStyle.backgroundColor || '';
163
- const cellBgAttr = (0, common_1.buildOutlookBgAttr)(cellBgColor);
166
+ const cellHasBorderRadius = Boolean(cellStyle.borderRadius);
167
+ const canApplyGridBgFallback = !cellHasBorderRadius && !divBorderStyle;
168
+ const effectiveCellBg = cellBgColor || (canApplyGridBgFallback ? msoBgColor : '');
169
+ const cellBgAttr = (0, common_1.buildOutlookBgAttr)(effectiveCellBg);
164
170
  html += `
165
171
  <td
166
172
  width="${Math.max(1, Math.round(widthPercent))}%"${cellBgAttr}
@@ -173,18 +179,22 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
173
179
  // Outlook mobile (which strips <style>) treats it proportionally, not as a fixed blocker.
174
180
  // col-gap-spacer class hides it when columns stack via CSS media query.
175
181
  if (columnGap > 0 && c !== lastVisibleCol) {
176
- html += `<td width="${columnGap}" class="col-gap-spacer" style="font-size:0;line-height:0;padding:0;" aria-hidden="true"></td>`;
182
+ const gapBgAttr = divBorderStyle ? '' : (0, common_1.buildOutlookBgAttr)(msoBgColor);
183
+ html += `<td width="${columnGap}"${gapBgAttr} class="col-gap-spacer" style="font-size:0;line-height:0;padding:0;" aria-hidden="true"></td>`;
177
184
  }
178
185
  }
179
186
  }
180
187
  else {
188
+ const emptyBgAttr = divBorderStyle ? '' : (0, common_1.buildOutlookBgAttr)(msoBgColor);
181
189
  html += `
182
190
  <td width="${Math.max(1, Math.round(widthPercent))}%"
191
+ ${emptyBgAttr}
183
192
  ${responsive ? 'class="stack-column"' : ""}
184
193
  style="width:${widthPercent}%;vertical-align:top;">
185
194
  </td>`;
186
195
  if (columnGap > 0 && c !== lastVisibleCol) {
187
- html += `<td width="${columnGap}" class="col-gap-spacer" style="font-size:0;line-height:0;padding:0;" aria-hidden="true"></td>`;
196
+ const gapBgAttr = divBorderStyle ? '' : (0, common_1.buildOutlookBgAttr)(msoBgColor);
197
+ html += `<td width="${columnGap}"${gapBgAttr} class="col-gap-spacer" style="font-size:0;line-height:0;padding:0;" aria-hidden="true"></td>`;
188
198
  }
189
199
  }
190
200
  }
@@ -310,7 +320,7 @@ async function convertGridCellBlock(blockData, rootData, cellWidthPercent, paren
310
320
  }
311
321
  }
312
322
  const borderRadius = cellBorderRadius || 0;
313
- const bgColor = styleWithoutBorder?.backgroundColor || "transparent";
323
+ const bgColor = styleWithoutBorder?.backgroundColor || "";
314
324
  // Build border CSS for the div wrapper.
315
325
  // When the parent grid already has a divBorderStyle wrapper (border + border-radius +
316
326
  // overflow:hidden), the cell must NOT duplicate the same border/radius — that causes
@@ -332,8 +342,10 @@ async function convertGridCellBlock(blockData, rootData, cellWidthPercent, paren
332
342
  }
333
343
  const cellDivBorderStyle = cellDivBorderParts.join(' ');
334
344
  // Unconditional div — visible to all clients (Gmail, Outlook new/old, Apple Mail).
335
- // background-color on the div covers modern clients; bgcolor on <td> covers Old Outlook.
336
- 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};`] : [];
337
349
  const formatPad = (value) => (typeof value === 'number' ? `${value}px` : (value || '0'));
338
350
  const cellPadTop = formatPad(cellPad.top);
339
351
  const cellPadBottom = formatPad(cellPad.bottom);
@@ -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,9 +101,12 @@ 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;`;
109
+ : `background-image:url('${rawBgImageUrl}'); background-position:${bgPosition}; background-size:${bgSize}; background-repeat:${bgRepeat};`;
107
110
  const wrappedContent = `
108
111
  <table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation"
109
112
  style="border-collapse:collapse;width:100%;max-width:${msoWidth}px;" class="${visibilityClass}">
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/utils/common.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAqBvD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAIrD,CAAC;AAGF,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,UASA;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAQvE;AAYD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGxD"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/utils/common.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAqBvD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAIrD,CAAC;AAGF,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,UASA;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAQvE;AAcD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGxD"}
@@ -57,8 +57,11 @@ function encodeBlockPropsAttr(props) {
57
57
  function toOutlookBgColor(color) {
58
58
  if (!color || color === 'transparent')
59
59
  return '';
60
- const m = color.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*[\d.]+)?\s*\)/);
60
+ const m = color.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)/);
61
61
  if (m) {
62
+ const alpha = m[4] !== undefined ? parseFloat(m[4]) : 1;
63
+ if (alpha < 1)
64
+ return '';
62
65
  return '#' + [m[1], m[2], m[3]].map(n => parseInt(n).toString(16).padStart(2, '0')).join('');
63
66
  }
64
67
  return color;
@@ -141,17 +141,17 @@ const convertJsonToHtml = async (jsonData) => {
141
141
  }
142
142
  </style>
143
143
  </head>
144
- <body style="margin:0; padding:0; background-color:${canvasColor || '#ffffff'};">
145
- <center style="width:100%; background-color:${canvasColor || '#ffffff'};">
144
+ <body bgcolor="#ffffff" style="margin:0; padding:0; background-color:#ffffff;">
145
+ <center style="width:100%;">
146
146
 
147
- <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="${canvasColor || '#ffffff'}">
147
+ <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#ffffff">
148
148
  <tr>
149
- <td align="center">
149
+ <td align="center" bgcolor="#ffffff">
150
150
 
151
151
  <!--[if mso]>
152
152
  <table role="presentation" border="0" cellspacing="0" cellpadding="0" width="600">
153
153
  <tr>
154
- <td>
154
+ <td bgcolor="${canvasColor || '#ffffff'}">
155
155
  <![endif]-->
156
156
 
157
157
  <table
@@ -177,7 +177,7 @@ const convertJsonToHtml = async (jsonData) => {
177
177
  >
178
178
  <tbody>
179
179
  <tr>
180
- <td style="padding: ${top}px ${right}px ${bottom}px ${left}px; max-width: 600px; overflow: hidden; box-sizing: border-box; word-break: break-word; overflow-wrap: anywhere;">
180
+ <td bgcolor="${canvasColor || '#ffffff'}" style="padding: ${top}px ${right}px ${bottom}px ${left}px; max-width: 600px; overflow: hidden; box-sizing: border-box; word-break: break-word; overflow-wrap: anywhere;">
181
181
  ${blocksHtml.join("")}
182
182
  </td>
183
183
  </tr>
@@ -1 +1 @@
1
- {"version":3,"file":"outlookSupport.d.ts","sourceRoot":"","sources":["../../src/utils/outlookSupport.ts"],"names":[],"mappings":"AAKA,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOtD;AAOD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,UAyClB;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,EACf,mBAAmB,EAAE,MAAM,EAC3B,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,GAAQ,EACf,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,mBA2FrB;AAED,wBAAsB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAO7G"}
1
+ {"version":3,"file":"outlookSupport.d.ts","sourceRoot":"","sources":["../../src/utils/outlookSupport.ts"],"names":[],"mappings":"AAKA,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAStD;AAOD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,UAyClB;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,EACf,mBAAmB,EAAE,MAAM,EAC3B,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,GAAQ,EACf,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,mBA2FrB;AAED,wBAAsB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAe7G"}
@@ -11,8 +11,11 @@ const common_1 = require("./common");
11
11
  function toOutlookBgColor(color) {
12
12
  if (!color || color === 'transparent')
13
13
  return '';
14
- const m = color.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*[\d.]+)?\s*\)/);
14
+ const m = color.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)/);
15
15
  if (m) {
16
+ const alpha = m[4] !== undefined ? parseFloat(m[4]) : 1;
17
+ if (alpha < 1)
18
+ return '';
16
19
  return '#' + [m[1], m[2], m[3]].map(n => parseInt(n).toString(16).padStart(2, '0')).join('');
17
20
  }
18
21
  return color;
@@ -126,7 +129,7 @@ async function appendOutlookForImage(content, outerContainerWidth, innerContaine
126
129
  <table border="0" cellpadding="0" cellspacing="0" width="${vmlWidth}" style="width:${vmlWidth}px;">
127
130
  <tr>
128
131
  <td align="center" valign="top" width="${vmlWidth}" style="width:${vmlWidth}px;">
129
- <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}" />
132
+ <img src="${imageUrl}" alt="Image" border="0" width="${vmlWidth}" style="display:block; width:${vmlWidth}px; height:auto; max-width:${vmlWidth}px; ${borderStyleAttr}" />
130
133
  </td>
131
134
  </tr>
132
135
  </table>
@@ -140,10 +143,17 @@ async function appendOutlookForImage(content, outerContainerWidth, innerContaine
140
143
  `;
141
144
  }
142
145
  async function loadImageNaturalDimensions(imageUrl) {
143
- return new Promise((resolve, reject) => {
144
- const img = new Image();
145
- img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });
146
- img.onerror = () => reject(new Error(`Failed to load image: ${imageUrl}`));
147
- img.src = imageUrl;
148
- });
146
+ // Browser environment: use native Image element
147
+ if (typeof window !== 'undefined' && typeof Image !== 'undefined') {
148
+ return new Promise((resolve, reject) => {
149
+ const img = new Image();
150
+ img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });
151
+ img.onerror = () => reject(new Error(`Failed to load image: ${imageUrl}`));
152
+ img.src = imageUrl;
153
+ });
154
+ }
155
+ // Node.js (BE) environment: use jimp to fetch and read dimensions
156
+ const { Jimp } = await import('jimp');
157
+ const image = await Jimp.read(imageUrl);
158
+ return { width: image.bitmap.width, height: image.bitmap.height };
149
159
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "email-builder-utils",
3
- "version": "1.1.50",
3
+ "version": "1.1.53",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [