email-builder-utils 1.1.53 → 1.1.55

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.
@@ -33,6 +33,8 @@ interface IProps {
33
33
  shape?: string;
34
34
  hideOnDesktop?: boolean;
35
35
  hideOnMobile?: boolean;
36
+ cellWidthUnit?: 'px' | '%';
37
+ widthUnit?: 'px' | '%';
36
38
  }
37
39
  interface IStyle {
38
40
  [key: string]: any;
@@ -1 +1 @@
1
- {"version":3,"file":"Template.d.ts","sourceRoot":"","sources":["../../src/types/Template.ts"],"names":[],"mappings":"AAAA,oBAAY,SAAS;IACnB,IAAI,SAAS;IACb,KAAK,UAAU;IACf,MAAM,WAAW;IACjB,IAAI,YAAY;IAChB,KAAK,UAAU;IACf,QAAQ,WAAW;IACnB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,WAAW,gBAAgB;IAC3B,KAAK,UAAU;IACf,KAAK,UAAU;IACf,QAAQ,aAAa;CACtB;AAED,oBAAY,UAAU;IACpB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,WAAW,gBAAgB;CAC5B;AAED,UAAU,MAAM;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,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,MAAM;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;CACH"}
1
+ {"version":3,"file":"Template.d.ts","sourceRoot":"","sources":["../../src/types/Template.ts"],"names":[],"mappings":"AAAA,oBAAY,SAAS;IACnB,IAAI,SAAS;IACb,KAAK,UAAU;IACf,MAAM,WAAW;IACjB,IAAI,YAAY;IAChB,KAAK,UAAU;IACf,QAAQ,WAAW;IACnB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,WAAW,gBAAgB;IAC3B,KAAK,UAAU;IACf,KAAK,UAAU;IACf,QAAQ,aAAa;CACtB;AAED,oBAAY,UAAU;IACpB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,WAAW,gBAAgB;CAC5B;AAED,UAAU,MAAM;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,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;IACvB,aAAa,CAAC,EAAE,IAAI,GAAG,GAAG,CAAC;IAC3B,SAAS,CAAC,EAAE,IAAI,GAAG,GAAG,CAAC;CACxB;AAED,UAAU,MAAM;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;CACH"}
@@ -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;;;GA0G5B"}
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,mBA2TtB;AAED,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,GAAG,EACd,QAAQ,EAAE,GAAG,EACb,gBAAgB,EAAE,MAAM,EACxB,iBAAiB,EAAE,MAAM,EACzB,mBAAmB,UAAQ;;;GAwG5B"}
@@ -9,7 +9,7 @@ const jsonToHTML_1 = require("../jsonToHTML");
9
9
  async function convertGridBlock(blockData, rootData, cellWidthInPx) {
10
10
  const { style = {}, childrenIds = [], props } = blockData.data;
11
11
  const { columns = 1, cellWidths = [], responsive = true } = props;
12
- const { columnGap = 0, backgroundImage, backgroundColor, ...restStyle } = style;
12
+ const { columnGap = 0, backgroundImage, backgroundColor, cellWidthUnit, ...restStyle } = style;
13
13
  const gridVisibilityClass = (0, common_1.getVisibilityClass)(props);
14
14
  // Detect gradient — check both backgroundImage prop and customCss (gradient may land in
15
15
  // customCss when the block was built via CSS shorthand or custom CSS input).
@@ -64,6 +64,16 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
64
64
  perChanges: [],
65
65
  pxChanges: buildStyles_1.allPxAttributes,
66
66
  });
67
+ // When cellWidthUnit is 'px', convert the stored pixel values to percentage ratios
68
+ // so the rest of the grid export logic (which works entirely in %) is unchanged.
69
+ const effectiveCellWidths = (cellWidthUnit || '%') === 'px'
70
+ ? (() => {
71
+ const total = cellWidths.reduce((s, w) => s + w, 0);
72
+ return total > 0
73
+ ? cellWidths.map((w) => Number(((w / total) * 100).toFixed(2)))
74
+ : cellWidths.map(() => 100 / columns);
75
+ })()
76
+ : cellWidths;
67
77
  const total = childrenIds.length;
68
78
  const visualRows = Math.ceil(total / columns);
69
79
  // OUTLOOK FIX: Use explicit pixel width for Old Outlook (Word engine)
@@ -129,7 +139,7 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
129
139
  const cellWidthPercents = [];
130
140
  for (let c = 0; c < columns; c++) {
131
141
  const id = rowIds[c];
132
- let widthPercent = cellWidths[c] ?? safeWidth;
142
+ let widthPercent = effectiveCellWidths[c] ?? safeWidth;
133
143
  if (widthPercent <= 0 || widthPercent > 100) {
134
144
  widthPercent = safeWidth;
135
145
  }
@@ -157,16 +167,10 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
157
167
  const visibilityClass = (0, common_1.getVisibilityClass)(childProps);
158
168
  if (childVisible) {
159
169
  const { html: childHtml, styles } = await convertGridCellBlock(child, rootData, widthPercent, adjustedTableWidth, Boolean(divBorderStyle));
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).
170
+ // bgcolor on the cell <td> ensures background-color survives Outlook
171
+ // compose paste (Word/Web editors strip CSS but keep bgcolor attribute).
165
172
  const cellBgColor = cellStyle.backgroundColor || '';
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);
173
+ const cellBgAttr = (0, common_1.buildOutlookBgAttr)(cellBgColor);
170
174
  html += `
171
175
  <td
172
176
  width="${Math.max(1, Math.round(widthPercent))}%"${cellBgAttr}
@@ -179,22 +183,18 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
179
183
  // Outlook mobile (which strips <style>) treats it proportionally, not as a fixed blocker.
180
184
  // col-gap-spacer class hides it when columns stack via CSS media query.
181
185
  if (columnGap > 0 && c !== lastVisibleCol) {
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>`;
186
+ html += `<td width="${columnGap}" class="col-gap-spacer" style="font-size:0;line-height:0;padding:0;" aria-hidden="true"></td>`;
184
187
  }
185
188
  }
186
189
  }
187
190
  else {
188
- const emptyBgAttr = divBorderStyle ? '' : (0, common_1.buildOutlookBgAttr)(msoBgColor);
189
191
  html += `
190
192
  <td width="${Math.max(1, Math.round(widthPercent))}%"
191
- ${emptyBgAttr}
192
193
  ${responsive ? 'class="stack-column"' : ""}
193
194
  style="width:${widthPercent}%;vertical-align:top;">
194
195
  </td>`;
195
196
  if (columnGap > 0 && c !== lastVisibleCol) {
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>`;
197
+ html += `<td width="${columnGap}" class="col-gap-spacer" style="font-size:0;line-height:0;padding:0;" aria-hidden="true"></td>`;
198
198
  }
199
199
  }
200
200
  }
@@ -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 || "";
323
+ const bgColor = styleWithoutBorder?.backgroundColor || "transparent";
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,10 +342,8 @@ 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 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};`] : [];
345
+ // background-color on the div covers modern clients; bgcolor on <td> covers Old Outlook.
346
+ const divStyleParts = [`background-color:${bgColor};`];
349
347
  const formatPad = (value) => (typeof value === 'number' ? `${value}px` : (value || '0'));
350
348
  const cellPadTop = formatPad(cellPad.top);
351
349
  const cellPadBottom = formatPad(cellPad.bottom);
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/utils/blocks/image.ts"],"names":[],"mappings":"AAIA,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,mBAAmB,EAAE,MAAM;;;;;GAoB5B;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAiH5E"}
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/utils/blocks/image.ts"],"names":[],"mappings":"AAIA,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,mBAAmB,EAAE,MAAM;;;;;GAoB5B;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAyH5E"}
@@ -28,24 +28,31 @@ async function convertImageBlock(blockData, cellWidthInPx) {
28
28
  const { style, props } = blockData.data;
29
29
  const { altText, imageUrl, navigateToUrl } = props;
30
30
  const visibilityClass = (0, common_1.getVisibilityClass)(props);
31
- const { width, height, objectFit, borderRadius, borderWidth, borderColor, borderStyle, ...containerStyle } = style;
31
+ const { width, widthUnit, height, objectFit, borderRadius, borderWidth, borderColor, borderStyle, ...containerStyle } = style;
32
32
  // Add border styles to container for fallback clients
33
33
  const containerStyles = (0, buildStyles_1.buildStyles)({
34
34
  ...containerStyle,
35
35
  }, { perChanges: [], pxChanges: buildStyles_1.addPxToAttributes });
36
36
  // OUTLOOK FIX: Ensure cellWidthInPx never exceeds 600px
37
37
  const safeCellWidth = Math.min(cellWidthInPx, 600);
38
- // Parse width percentage (default 100%)
39
- const widthPercent = typeof width === "string" && width.includes("%")
40
- ? parseInt(width.replace("%", ""))
41
- : typeof width === "number"
42
- ? width
43
- : 100;
44
38
  // OUTLOOK FIX: Calculate inner container width based on safe cell width
45
39
  const paddingLeft = style?.padding?.left || 0;
46
40
  const paddingRight = style?.padding?.right || 0;
47
41
  const availableWidth = safeCellWidth - paddingLeft - paddingRight;
48
- const innerContainerWidth = Math.round((widthPercent / 100) * availableWidth);
42
+ // Resolve inner container width px values are capped to the available space;
43
+ // % values are a fraction of available space.
44
+ let innerContainerWidth;
45
+ if ((widthUnit || '%') === 'px') {
46
+ innerContainerWidth = Math.min(Math.round(Number(width) || availableWidth), availableWidth);
47
+ }
48
+ else {
49
+ const widthPercent = typeof width === "string" && width.includes("%")
50
+ ? parseInt(width.replace("%", ""))
51
+ : typeof width === "number"
52
+ ? width
53
+ : 100;
54
+ innerContainerWidth = Math.round((widthPercent / 100) * availableWidth);
55
+ }
49
56
  // Get image dimensions and calculate scaled sizes
50
57
  const { originalWidth, originalHeight, scaledWidth, scaledHeight } = await computeScaledDimensions(imageUrl, innerContainerWidth);
51
58
  // OUTLOOK FIX: For Outlook, we need exact pixel dimensions
@@ -71,11 +78,13 @@ async function convertImageBlock(blockData, cellWidthInPx) {
71
78
  // -ms-interpolation-mode → inline fallback; the <style> block is stripped by
72
79
  // Outlook during forward/reply MIME rewriting
73
80
  const imageElement = `<img src="${imageUrl}" alt="${altText || "Image"}" border="0" width="${finalWidth}" height="${finalHeight}" style="${imageTagStyles}; display:block; width:${finalWidth}px; height:${finalHeight}px; max-width:100%; line-height:0; -ms-interpolation-mode:bicubic;" />`;
74
- const percentWidth = typeof width === "string" && width.endsWith("%")
75
- ? width
76
- : typeof width === "number"
77
- ? `${width}%`
78
- : "100%";
81
+ const percentWidth = (widthUnit || '%') === 'px'
82
+ ? `${innerContainerWidth}px`
83
+ : typeof width === "string" && width.endsWith("%")
84
+ ? width
85
+ : typeof width === "number"
86
+ ? `${width}%`
87
+ : "100%";
79
88
  // Non-MSO wrapper: display:block removes the phantom inline-baseline gap that
80
89
  // display:inline-block creates in Gmail / Apple Mail / Yahoo between images.
81
90
  // margin handles alignment since text-align won't move block elements.
@@ -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,UAqLtE"}
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,UAsKtE"}
@@ -13,24 +13,22 @@ function convertTextBlock(blockData, cellWidthInPx) {
13
13
  // Detect background image or gradient (may live in backgroundImage or customCss).
14
14
  // Same multi-client approach as Grid block: outer wrapper <td> carries the background
15
15
  // via CSS (Gmail/New Outlook), background attribute (Yahoo), and VML (Old Outlook).
16
- const bgImageStr = typeof backgroundImage === "string" ? backgroundImage : "";
17
- const customCssStr = rest.customCss || "";
18
- const gradientInCustomCss = !bgImageStr.includes("gradient(") && customCssStr.includes("gradient(")
19
- ? customCssStr.match(/(?:linear|radial|conic)-gradient\([^)]+(?:\([^)]*\)[^)]*)*\)/)?.[0] || ""
20
- : "";
21
- const effectiveGradient = bgImageStr.includes("gradient(")
22
- ? bgImageStr
23
- : gradientInCustomCss;
16
+ const bgImageStr = typeof backgroundImage === 'string' ? backgroundImage : '';
17
+ const customCssStr = rest.customCss || '';
18
+ const gradientInCustomCss = !bgImageStr.includes('gradient(') && customCssStr.includes('gradient(')
19
+ ? (customCssStr.match(/(?:linear|radial|conic)-gradient\([^)]+(?:\([^)]*\)[^)]*)*\)/)?.[0] || '')
20
+ : '';
21
+ const effectiveGradient = bgImageStr.includes('gradient(') ? bgImageStr : gradientInCustomCss;
24
22
  const isGradient = Boolean(effectiveGradient);
25
23
  const parsedGradient = isGradient ? (0, gradientUtils_1.parseGradient)(effectiveGradient) : null;
26
24
  const rawBgImageUrl = !isGradient && bgImageStr
27
- ? bgImageStr.replace(/^url\(['"]?/, "").replace(/['"]?\)$/, "")
25
+ ? bgImageStr.replace(/^url\(['"]?/, '').replace(/['"]?\)$/, '')
28
26
  : null;
29
27
  const hasBgImage = Boolean(rawBgImageUrl || isGradient);
30
28
  const fallbackBgColor = textContainerBackgroundColor ||
31
29
  parsedGradient?.fallback ||
32
30
  (0, gradientUtils_1.extractCssFallbackColor)(customCssStr) ||
33
- "#ffffff";
31
+ '#ffffff';
34
32
  // Text box decoration styles (border, background, padding) — no width
35
33
  const textBoxStyle = {
36
34
  backgroundColor,
@@ -46,11 +44,9 @@ function convertTextBlock(blockData, cellWidthInPx) {
46
44
  });
47
45
  // Strip gradient from customCss when it is hoisted to the outer bg wrapper.
48
46
  const innerCustomCss = gradientInCustomCss
49
- ? customCssStr.replace(/background-image\s*:[^;]+;?/gi, "").trim()
47
+ ? customCssStr.replace(/background-image\s*:[^;]+;?/gi, '').trim()
50
48
  : customCssStr;
51
- const restForStyles = gradientInCustomCss
52
- ? { ...rest, customCss: innerCustomCss }
53
- : rest;
49
+ const restForStyles = gradientInCustomCss ? { ...rest, customCss: innerCustomCss } : rest;
54
50
  // Outer td styles: strip container background when a bg-image wrapper is present
55
51
  // so the outer wrapper's background is not double-applied.
56
52
  const styles = (0, buildStyles_1.buildStyles)({
@@ -62,57 +58,53 @@ function convertTextBlock(blockData, cellWidthInPx) {
62
58
  pxChanges: buildStyles_1.allPxAttributes,
63
59
  });
64
60
  const sanitizedText = (props.text ?? "")
65
- .replace(/<p(\s[^>]*)?>/gi, (_, attrs) => `<div${attrs || ""}>`)
61
+ .replace(/<p(\s[^>]*)?>/gi, (_, attrs = "") => `<div${attrs || ""}>`)
66
62
  .replace(/<\/p>/gi, "</div>");
67
63
  const navigateToUrl = props.navigateToUrl || "";
68
64
  const fontSizeStyle = fontSize != null ? `font-size:${fontSize}px;` : "";
69
- // Email clients apply `a { color: blue }` which overrides inherited color.
70
- // Inject the block color directly onto <a> tags that don't already have one.
65
+ // Links default to standard link blue + underline. Preserve any color or
66
+ // text-decoration the user explicitly set; only inject what is missing.
71
67
  const blockTextColor = rest.color;
72
- const processedText = blockTextColor
73
- ? sanitizedText.replace(/<a(\s[^>]*)?>/gi, (match, attrs = "") => {
74
- if (/style\s*=\s*["'][^"']*\bcolor\s*:/i.test(attrs))
75
- return match;
76
- if (/\bstyle\s*=/i.test(attrs)) {
77
- return `<a${attrs.replace(/(\bstyle\s*=\s*["'])/, `$1color:${blockTextColor};`)}>`;
78
- }
79
- return `<a${attrs} style="color:${blockTextColor};">`;
80
- })
81
- : sanitizedText;
82
- const colorStyle = blockTextColor ? `color:${blockTextColor};` : "";
68
+ const processedText = sanitizedText.replace(/<a(\s[^>]*)?>/gi, (_match, attrs = '') => {
69
+ const hasExistingColor = /style\s*=\s*["'][^"']*\bcolor\s*:/i.test(attrs);
70
+ const hasExistingDecoration = /style\s*=\s*["'][^"']*\btext-decoration\s*:/i.test(attrs);
71
+ const colorPart = hasExistingColor ? '' : 'color:#0066CC;';
72
+ const decorationPart = hasExistingDecoration ? '' : 'text-decoration:underline;';
73
+ const injected = `${colorPart}${decorationPart}`;
74
+ if (!injected)
75
+ return `<a${attrs}>`;
76
+ if (/\bstyle\s*=/i.test(attrs)) {
77
+ return `<a${attrs.replace(/(\bstyle\s*=\s*["'])/, `$1${injected}`)}>`;
78
+ }
79
+ return `<a${attrs} style="${injected}">`;
80
+ });
81
+ const colorStyle = blockTextColor ? `color:${blockTextColor};` : '';
83
82
  // Use display:block + width:100% so text fills the column naturally.
84
83
  // display:inline-block with a pixel width (e.g. 400px) breaks narrow grid cells.
85
84
  const convertedTextBox = `<div style="display:block; width:100%; box-sizing:border-box; ${colorStyle}${fontSizeStyle}${convertedTextStyle}">${processedText.replaceAll(/\n/g, "<br>")}</div>`;
86
- const safeCellWidth = cellWidthInPx
87
- ? Math.min(cellWidthInPx, 600)
88
- : undefined;
85
+ const safeCellWidth = cellWidthInPx ? Math.min(cellWidthInPx, 600) : undefined;
89
86
  // When a bg-image wrapper is present, visibilityClass moves to the outer table.
90
- const textContent = (0, outlookSupport_1.appendOutlookSupport)(convertedTextBox, styles, hasBgImage ? "" : visibilityClass, safeCellWidth);
91
- const linkColorStyle = blockTextColor
92
- ? `color:${blockTextColor};`
93
- : "color:inherit;";
87
+ const textContent = (0, outlookSupport_1.appendOutlookSupport)(convertedTextBox, styles, hasBgImage ? '' : visibilityClass, safeCellWidth);
88
+ const linkColorStyle = blockTextColor ? `color:${blockTextColor};` : 'color:inherit;';
94
89
  if (hasBgImage) {
95
90
  const msoWidth = cellWidthInPx ? Math.min(cellWidthInPx, 600) : 600;
96
91
  const vmlFill = isGradient
97
92
  ? (() => {
98
93
  const vmlAngle = (0, gradientUtils_1.cssAngleToVml)(parsedGradient?.angle || 180);
99
- const c1 = parsedGradient?.fallback || "#ffffff";
94
+ const c1 = parsedGradient?.fallback || '#ffffff';
100
95
  const c2 = parsedGradient?.colors[parsedGradient.colors.length - 1] || c1;
101
96
  return `<v:fill type="gradient" color="${c1}" color2="${c2}" angle="${vmlAngle}" />`;
102
97
  })()
103
98
  : `<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';
107
99
  const bgCss = isGradient
108
100
  ? `background:${effectiveGradient};`
109
- : `background-image:url('${rawBgImageUrl}'); background-position:${bgPosition}; background-size:${bgSize}; background-repeat:${bgRepeat};`;
101
+ : `background-image:url('${rawBgImageUrl}'); background-position:center center; background-size:cover; background-repeat:no-repeat;`;
110
102
  const wrappedContent = `
111
103
  <table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation"
112
104
  style="border-collapse:collapse;width:100%;max-width:${msoWidth}px;" class="${visibilityClass}">
113
105
  <tr>
114
106
  <td width="100%"${(0, common_1.buildOutlookBgAttr)(fallbackBgColor)} valign="top"
115
- ${!isGradient && rawBgImageUrl ? `background="${rawBgImageUrl}"` : ""}
107
+ ${!isGradient && rawBgImageUrl ? `background="${rawBgImageUrl}"` : ''}
116
108
  style="width:100%;max-width:${msoWidth}px;background-color:${fallbackBgColor};${bgCss}">
117
109
 
118
110
  <!--[if gte mso 9]>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "email-builder-utils",
3
- "version": "1.1.53",
3
+ "version": "1.1.55",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [