@pagenflow/email 1.4.1 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -8,41 +8,49 @@ function Body({ children, config = {} }) {
8
8
  const globalFontSize = config.fontSize || "16px";
9
9
  const globalBackgroundColor = config.backgroundColor || "#ffffff";
10
10
  const globalLineHeight = config.lineHeight || "1.4";
11
+ const globalFontFamily = config.fontFamily || "Arial, Helvetica, sans-serif";
11
12
  // Background image properties
12
13
  const bgImage = ((_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.src) || "";
13
14
  const bgRepeat = ((_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.repeat) || "no-repeat";
14
15
  const bgSize = ((_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.size) || "cover";
15
16
  const bgPosition = ((_d = config.backgroundImage) === null || _d === void 0 ? void 0 : _d.position) || "center";
16
17
  // 1. Style for the <body> tag inline
17
- const bodyStyle = Object.assign({ backgroundColor: globalBackgroundColor, color: globalColor, fontSize: globalFontSize, lineHeight: globalLineHeight, padding: "0", margin: "0", WebkitTextSizeAdjust: "100%", overflowX: "hidden",
18
- // ✅ FIX 1: Use string indexing for MSO property
19
- // ['ms-text-size-adjust' as string]: '100%',
20
- ["msTextSizeAdjust"]: "100%",
21
- // ["mso-line-height-rule" as string]: "exactly",
22
- ["msoLineHeightRule"]: "exactly",
23
- // Base font for body
24
- fontFamily: "Arial, Helvetica, sans-serif" }, (bgImage && {
25
- backgroundImage: `url(${bgImage})`,
26
- backgroundRepeat: bgRepeat,
27
- backgroundSize: bgSize,
28
- backgroundPosition: bgPosition,
29
- }));
18
+ const bodyStyle = {
19
+ backgroundColor: globalBackgroundColor,
20
+ color: globalColor,
21
+ fontSize: globalFontSize,
22
+ lineHeight: globalLineHeight,
23
+ padding: "0",
24
+ margin: "0",
25
+ WebkitTextSizeAdjust: "100%",
26
+ overflowX: "hidden",
27
+ ["msTextSizeAdjust"]: "100%",
28
+ ["msoLineHeightRule"]: "exactly",
29
+ fontFamily: globalFontFamily,
30
+ // Background image support (if provided)
31
+ ...(bgImage && {
32
+ backgroundImage: `url(${bgImage})`,
33
+ backgroundRepeat: bgRepeat,
34
+ backgroundSize: bgSize,
35
+ backgroundPosition: bgPosition,
36
+ }),
37
+ };
30
38
  // 2. Style for the top-level <table> wrapper
31
39
  const outerTableStyle = {
32
40
  width: "100%",
33
- // ✅ FIX 1 (on table): Use string indexing for MSO property
34
41
  ["msoLineHeightRule"]: "exactly",
35
- // ['mso-line-height-rule' as string]: 'exactly',
36
42
  borderCollapse: "collapse",
37
43
  };
38
- return (
39
- // The <body> tag with inline styles
40
- jsxs("body", { style: bodyStyle, children: [jsx("center", { style: Object.assign({ width: "100%", background: globalBackgroundColor }, (bgImage && {
41
- backgroundImage: `url(${bgImage})`,
42
- backgroundRepeat: bgRepeat,
43
- backgroundSize: bgSize,
44
- backgroundPosition: bgPosition,
45
- })), children: jsx("table", { role: "presentation", border: 0, cellPadding: 0, cellSpacing: 0, align: "center", width: "100%", style: outerTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { align: "center", style: { padding: "0", margin: "0" }, children: children }) }) }) }) }), jsx("div", { style: {
44
+ return (jsxs("body", { style: bodyStyle, children: [jsx("center", { style: {
45
+ width: "100%",
46
+ background: globalBackgroundColor,
47
+ ...(bgImage && {
48
+ backgroundImage: `url(${bgImage})`,
49
+ backgroundRepeat: bgRepeat,
50
+ backgroundSize: bgSize,
51
+ backgroundPosition: bgPosition,
52
+ }),
53
+ }, children: jsx("table", { role: "presentation", border: 0, cellPadding: 0, cellSpacing: 0, align: "center", width: "100%", style: outerTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { align: "center", style: { padding: "0", margin: "0" }, children: children }) }) }) }) }), jsx("div", { style: {
46
54
  display: "none",
47
55
  whiteSpace: "nowrap",
48
56
  font: "15px courier",
@@ -1529,41 +1537,6 @@ const justifyMap$3 = {
1529
1537
  center: "center",
1530
1538
  end: "right",
1531
1539
  };
1532
- function getBorderStyle$6(border) {
1533
- if (!border)
1534
- return {};
1535
- const style = {};
1536
- // If a full border is specified, apply it
1537
- if (border.width && border.style && border.color) {
1538
- style.border = `${border.width} ${border.style} ${border.color}`;
1539
- }
1540
- else {
1541
- // If only individual borders are specified, explicitly set others to 'none'
1542
- // to prevent Outlook Classic from showing black borders
1543
- const hasIndividualBorders = border.top || border.right || border.bottom || border.left;
1544
- if (hasIndividualBorders) {
1545
- // Default all borders to none
1546
- style.borderTop = "none";
1547
- style.borderRight = "none";
1548
- style.borderBottom = "none";
1549
- style.borderLeft = "none";
1550
- }
1551
- }
1552
- // Override with specific borders if provided
1553
- if (border.top) {
1554
- style.borderTop = `${border.top.width} ${border.top.style} ${border.top.color}`;
1555
- }
1556
- if (border.right) {
1557
- style.borderRight = `${border.right.width} ${border.right.style} ${border.right.color}`;
1558
- }
1559
- if (border.bottom) {
1560
- style.borderBottom = `${border.bottom.width} ${border.bottom.style} ${border.bottom.color}`;
1561
- }
1562
- if (border.left) {
1563
- style.borderLeft = `${border.left.width} ${border.left.style} ${border.left.color}`;
1564
- }
1565
- return style;
1566
- }
1567
1540
  function getBorderStyleString$2(border) {
1568
1541
  if (!border)
1569
1542
  return "";
@@ -1605,10 +1578,16 @@ function Button({ config, devMode }) {
1605
1578
  const safeFontFamily = fontFamily
1606
1579
  ? fontFamily.replace(/['"]/g, "")
1607
1580
  : fontFamily;
1608
- // 2. Outer TD Style for Background and Border Radius (no border)
1609
- const backgroundTdStyle = Object.assign(Object.assign({ backgroundColor: backgroundColor, borderRadius: borderRadius, width: width || "auto" }, (maxWidth && { maxWidth: maxWidth })), (borderRadius && { overflow: "hidden" }));
1610
- // 3. Border styles
1611
- getBorderStyle$6(border);
1581
+ // Outer TD Style for Background and Border Radius (no border)
1582
+ const backgroundTdStyle = {
1583
+ backgroundColor: backgroundColor,
1584
+ borderRadius: borderRadius,
1585
+ width: width || "auto",
1586
+ ...(maxWidth && { maxWidth: maxWidth }),
1587
+ // Overflow hidden to clip background to border-radius
1588
+ ...(borderRadius && { overflow: "hidden" }),
1589
+ };
1590
+ // Border styles
1612
1591
  const borderStyleString = getBorderStyleString$2(border);
1613
1592
  // --- Determine Button Approach Based on Width ---
1614
1593
  // Check if width is percentage-based or not defined
@@ -1679,9 +1658,12 @@ function Button({ config, devMode }) {
1679
1658
  else {
1680
1659
  vmlAlignStyle = `text-align:${textAlign};`;
1681
1660
  }
1661
+ // Border radius is intentionally omitted (arcsize="0%") for Outlook Classic.
1662
+ // Outlook Classic does not reliably support rounded corners and the result
1663
+ // is inconsistent, so we render sharp corners there instead.
1682
1664
  vmlButton = `
1683
1665
  <!--[if mso]>
1684
- <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${href}" style="height:${vmlHeight}px;width:${vmlWidth}px;" arcsize="${Math.min((parseInt(borderRadius) / vmlHeight) * 100, 50)}%" strokecolor="${vmlStrokeColor}" ${hasVmlStroke ? `strokeweight="${vmlStrokeWeight}px"` : 'stroke="f"'} fillcolor="${vmlFillColor}">
1666
+ <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${href}" style="height:${vmlHeight}px;width:${vmlWidth}px;" arcsize="0%" strokecolor="${vmlStrokeColor}" ${hasVmlStroke ? `strokeweight="${vmlStrokeWeight}px"` : 'stroke="f"'} fillcolor="${vmlFillColor}">
1685
1667
  <w:anchorlock/>
1686
1668
  <v:textbox inset="${horizontalPaddingValue}px,${numericPadding}px,${horizontalPaddingValue}px,${numericPadding}px">
1687
1669
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse:collapse;">
@@ -1714,6 +1696,9 @@ function Button({ config, devMode }) {
1714
1696
  const directionProp = direction ? `direction: ${direction};` : "";
1715
1697
  const opacityProp = opacity !== undefined ? `opacity: ${opacity};` : "";
1716
1698
  const wordBreakProp = wordBreak !== "break-word" ? `word-break: ${wordBreak};` : "";
1699
+ // Border radius is intentionally omitted from the Outlook Classic table cell.
1700
+ // Outlook Classic ignores border-radius on table cells anyway, and including it
1701
+ // can cause unexpected rendering artifacts, so we explicitly leave it out.
1717
1702
  simpleOutlookButton = `
1718
1703
  <!--[if mso]>
1719
1704
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse;">
@@ -1721,7 +1706,7 @@ function Button({ config, devMode }) {
1721
1706
  <td align="${align}" style="padding: 0;">
1722
1707
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="${width || "auto"}" style="border-collapse: collapse;">
1723
1708
  <tr>
1724
- <td bgcolor="${backgroundColor}" align="${textAlign}" style="padding: ${padding}; text-align: ${textAlign}; border-radius: ${borderRadius}; ${borderStyleString}">
1709
+ <td bgcolor="${backgroundColor}" align="${textAlign}" style="padding: ${padding}; text-align: ${textAlign}; ${borderStyleString}">
1725
1710
  <a href="${href}" target="_blank" rel="noopener noreferrer" style="color: ${color}; ${textDecorationStyle} display: block; font-family: ${safeFontFamily}; font-size: ${fontSize}; font-weight: ${fontWeight}; ${fontStyleProp} line-height: ${lineHeight}; ${letterSpacingProp} ${textTransformProp} text-align: ${textAlign}; ${whiteSpaceProp} ${directionProp} ${opacityProp} ${wordBreakProp} mso-line-height-rule: exactly;">
1726
1711
  ${typeof children === "string" ? children : ""}
1727
1712
  </a>
@@ -1766,15 +1751,21 @@ function Button({ config, devMode }) {
1766
1751
  padding: 0,
1767
1752
  }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { align: align, style: {
1768
1753
  padding: 0,
1769
- }, children: jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: Object.assign(Object.assign({
1754
+ }, children: jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
1770
1755
  // --- Start dev
1771
- position: "relative",
1756
+ position: "relative",
1772
1757
  // --- End dev
1773
- width: width || "auto" }, (maxWidth && { maxWidth: maxWidth })), { borderCollapse: "collapse",
1758
+ width: width || "auto",
1759
+ ...(maxWidth && { maxWidth: maxWidth }),
1760
+ borderCollapse: "collapse",
1774
1761
  // base
1775
- boxSizing: "border-box", border: 0, margin: 0, padding: 0 }), onClick: devMode ? (e) => e.preventDefault() : undefined, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { dangerouslySetInnerHTML: {
1762
+ boxSizing: "border-box",
1763
+ border: 0,
1764
+ margin: 0,
1765
+ padding: 0,
1766
+ }, onClick: devMode ? (e) => e.preventDefault() : undefined, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { dangerouslySetInnerHTML: {
1776
1767
  __html: `
1777
- ${devMode ? "" : useSimpleOutlookApproach ? simpleOutlookButton : vmlButton}
1768
+ ${useSimpleOutlookApproach ? simpleOutlookButton : vmlButton}
1778
1769
  <!--[if !mso]><!-->
1779
1770
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse; width: 100%;">
1780
1771
  <tbody>
@@ -1785,11 +1776,11 @@ function Button({ config, devMode }) {
1785
1776
  <tr>
1786
1777
  <td style="padding: 0;">
1787
1778
  ${devMode
1788
- ? `<span style="${sharedTextStyles} display: block; text-align: ${textAlign}; word-break: ${wordBreak}; padding: ${padding};">
1779
+ ? `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; word-break: ${wordBreak}; text-align: ${textAlign}; padding: ${padding};">
1789
1780
  ${typeof children === "string" ? children : ""}
1790
1781
  </span>`
1791
1782
  : `<a href="${href}" target="_blank" rel="noopener noreferrer" style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; word-break: ${wordBreak}; text-align: ${textAlign}; padding: ${padding};">
1792
- <span style="${sharedTextStyles}">
1783
+ <span>
1793
1784
  ${typeof children === "string" ? children : ""}
1794
1785
  </span>
1795
1786
  </a>`}
@@ -1869,14 +1860,31 @@ function Column({ children, config, devNode }) {
1869
1860
  };
1870
1861
  // 2. Outer TD style: Background and Border Radius (no border here).
1871
1862
  // height is set so the TD occupies the full declared height.
1872
- const outerTdStyle = Object.assign({ width: config.width, height: config.height, backgroundColor: config.backgroundColor, borderRadius: config.borderRadius,
1863
+ const outerTdStyle = {
1864
+ width: config.width,
1865
+ height: config.height,
1866
+ backgroundColor: config.backgroundColor,
1867
+ borderRadius: config.borderRadius,
1873
1868
  // Background Image styles
1874
1869
  backgroundImage: config.backgroundImage
1875
1870
  ? `url(${config.backgroundImage.src})`
1876
- : undefined, backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat, backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size, backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position }, (config.borderRadius && { overflow: "hidden" }));
1871
+ : undefined,
1872
+ backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat,
1873
+ backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size,
1874
+ backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position,
1875
+ // Overflow hidden to clip background to border-radius
1876
+ ...(config.borderRadius && { overflow: "hidden" }),
1877
+ };
1877
1878
  // 2b. Inner table style: Border and Border Radius.
1878
1879
  // height: 100% so it stretches to fill the outer TD's declared height.
1879
- const innerTableStyle = Object.assign({ width: "100%", height: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: config.borderRadius }, getBorderStyle$5(config.border));
1880
+ const innerTableStyle = {
1881
+ width: "100%",
1882
+ height: "100%", // fill the outer TD rather than re-declaring the pixel value
1883
+ borderCollapse: "separate",
1884
+ borderSpacing: 0,
1885
+ borderRadius: config.borderRadius,
1886
+ ...getBorderStyle$5(config.border),
1887
+ };
1880
1888
  // 3. Inner TD style: Padding and Vertical Alignment only.
1881
1889
  // *** No height here. ***
1882
1890
  // The outer TD/table owns the height; padding is purely inner spacing,
@@ -1908,7 +1916,10 @@ function Column({ children, config, devNode }) {
1908
1916
  : "top", align: config.alignItems
1909
1917
  ? alignMap$2[config.alignItems]
1910
1918
  : "left", children: child }) }), index < numChildren - 1 && (jsx("tr", { children: jsx("td", { style: gapSpacerStyle, children: "\u00A0" }) }))] }, `col-child-${index}`))) }) })) : (children) }) }) }) }));
1911
- return (jsxs("table", Object.assign({ "aria-label": "Column Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: Object.assign({ position: "relative" }, outerTableStyle) }, (config.height && { height: config.height }), { children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", Object.assign({ style: outerTdStyle }, (config.width && { width: config.width }), (config.height && { height: config.height }), { children: renderContent() })) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] })));
1919
+ return (jsxs("table", { "aria-label": "Column Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
1920
+ position: "relative",
1921
+ ...outerTableStyle,
1922
+ }, ...(config.height && { height: config.height }), children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: outerTdStyle, ...(config.width && { width: config.width }), ...(config.height && { height: config.height }), children: renderContent() }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
1912
1923
  }
1913
1924
  var Column_default = memo(Column, arePropsEqual);
1914
1925
 
@@ -2013,11 +2024,27 @@ function Container({ children, config, devMode, devNode }) {
2013
2024
  borderCollapse: "collapse",
2014
2025
  };
2015
2026
  // 1. Background TD Style - Background color, border radius, background image
2016
- const backgroundTdStyle = Object.assign({ backgroundColor: config.backgroundColor, borderRadius: config.borderRadius, maxWidth: widthType === "fixed" ? config.width || "600px" : undefined, backgroundImage: config.backgroundImage
2027
+ const backgroundTdStyle = {
2028
+ backgroundColor: config.backgroundColor,
2029
+ borderRadius: config.borderRadius,
2030
+ maxWidth: widthType === "fixed" ? config.width || "600px" : undefined,
2031
+ backgroundImage: config.backgroundImage
2017
2032
  ? `url(${config.backgroundImage.src})`
2018
- : undefined, backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat, backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size, backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position }, (config.borderRadius && { overflow: "hidden" }));
2033
+ : undefined,
2034
+ backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat,
2035
+ backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size,
2036
+ backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position,
2037
+ // Overflow hidden to clip background to border-radius
2038
+ ...(config.borderRadius && { overflow: "hidden" }),
2039
+ };
2019
2040
  // 2. Border Table Style - Border and border radius
2020
- const borderTableStyle = Object.assign({ width: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: config.borderRadius }, getBorderStyle$4(config.border));
2041
+ const borderTableStyle = {
2042
+ width: "100%",
2043
+ borderCollapse: "separate",
2044
+ borderSpacing: 0,
2045
+ borderRadius: config.borderRadius,
2046
+ ...getBorderStyle$4(config.border),
2047
+ };
2021
2048
  // 3. Padding TD Style
2022
2049
  const innerTdStyle = {
2023
2050
  padding: config.padding,
@@ -2057,7 +2084,10 @@ function Container({ children, config, devMode, devNode }) {
2057
2084
  }
2058
2085
  return (jsx("td", { className: isStacking ? "stack-td" : undefined, width: getChildWidths[index], style: childTdStyle, children: child }, `child-${index}`));
2059
2086
  });
2060
- return (jsx("table", { "aria-label": `Container | Table Outer`, cellPadding: 0, cellSpacing: 0, role: "presentation", border: 0, style: Object.assign({ position: "relative" }, outerTableStyle), children: jsx("tbody", { children: jsx("tr", { children: jsxs("td", { align: justifyAlign, children: [jsx("div", { dangerouslySetInnerHTML: { __html: msoFixedWrapper } }), jsxs("table", { className: [
2087
+ return (jsx("table", { "aria-label": `Container | Table Outer`, cellPadding: 0, cellSpacing: 0, role: "presentation", border: 0, style: {
2088
+ position: "relative",
2089
+ ...outerTableStyle,
2090
+ }, children: jsx("tbody", { children: jsx("tr", { children: jsxs("td", { align: justifyAlign, children: [jsx("div", { dangerouslySetInnerHTML: { __html: msoFixedWrapper } }), jsxs("table", { className: [
2061
2091
  widthType === "fixed" ? "container-fixed-width" : undefined,
2062
2092
  devMode ? "main-wrapper relative" : undefined,
2063
2093
  ]
@@ -2100,12 +2130,12 @@ function Divider({ config, devNode }) {
2100
2130
  // --- End dev
2101
2131
  width: "100%",
2102
2132
  borderCollapse: "collapse",
2103
- }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: outerTdStyle, align: align, children: jsx("table", Object.assign({ "aria-label": "Divider Line", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: align, style: dividerTableStyle }, { height: dividerHeightAttribute }, { children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: {
2133
+ }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: outerTdStyle, align: align, children: jsx("table", { "aria-label": "Divider Line", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: align, style: dividerTableStyle, ...{ height: dividerHeightAttribute }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: {
2104
2134
  height: height,
2105
2135
  fontSize: "0",
2106
2136
  lineHeight: "0",
2107
2137
  padding: "0",
2108
- }, children: "\u00A0" }) }) }) })) }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
2138
+ }, children: "\u00A0" }) }) }) }) }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
2109
2139
  }
2110
2140
  var Divider_default = memo(Divider, arePropsEqual);
2111
2141
 
@@ -2283,7 +2313,7 @@ function Head({ children, backgroundColor = "#ffffff", title = "Email Preview",
2283
2313
  }
2284
2314
  h1, h2, h3, h4, h5, h6 { margin: 0; padding: 0; font-weight: inherit; }
2285
2315
  `;
2286
- return (jsxs("head", { children: [jsx("meta", { httpEquiv: "Content-Type", content: "text/html; charset=utf-8" }), jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }), jsx("meta", { httpEquiv: "X-UA-Compatible", content: "IE=edge" }), jsx("title", { children: title }), fonts.flatMap((resolved) => resolved.fontProps.map((props, i) => (jsx(Font, Object.assign({}, props), `${resolved.family}-${props.fontWeight}-${props.fontStyle}-${i}`)))), children, jsx("style", { type: "text/css", dangerouslySetInnerHTML: { __html: msoResetStyles } }), jsx("style", { type: "text/css", dangerouslySetInnerHTML: { __html: globalStyles } })] }));
2316
+ return (jsxs("head", { children: [jsx("meta", { httpEquiv: "Content-Type", content: "text/html; charset=utf-8" }), jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }), jsx("meta", { httpEquiv: "X-UA-Compatible", content: "IE=edge" }), jsx("title", { children: title }), fonts.flatMap((resolved) => resolved.fontProps.map((props, i) => (jsx(Font, { ...props }, `${resolved.family}-${props.fontWeight}-${props.fontStyle}-${i}`)))), children, jsx("style", { type: "text/css", dangerouslySetInnerHTML: { __html: msoResetStyles } }), jsx("style", { type: "text/css", dangerouslySetInnerHTML: { __html: globalStyles } })] }));
2287
2317
  }
2288
2318
 
2289
2319
  function Heading({ config, devMode, children }) {
@@ -2350,28 +2380,6 @@ function Html({ children, backgroundColor = "#ffffff", }) {
2350
2380
  );
2351
2381
  }
2352
2382
 
2353
- /**
2354
- * Content rendered by Outlook Classic only.
2355
- * Outputs: <!--[if mso]> ... <![endif]-->
2356
- */
2357
- function MsoOnly({ html }) {
2358
- return (jsx("td", { dangerouslySetInnerHTML: {
2359
- __html: `<!--[if mso]>${html}<![endif]-->`,
2360
- } }));
2361
- }
2362
- /**
2363
- * Content hidden from Outlook Classic, visible in all other clients.
2364
- * Outputs: <!--[if !mso]><!--> ... <!--<![endif]-->
2365
- */
2366
- function NonMso({ html }) {
2367
- return (jsx("td", { dangerouslySetInnerHTML: {
2368
- __html: `<!--[if !mso]><!-->${html}<!--<![endif]-->`,
2369
- } }));
2370
- }
2371
-
2372
- // ---------------------------------------------------------------------------
2373
- // Helpers
2374
- // ---------------------------------------------------------------------------
2375
2383
  function getBorderStyle$3(border) {
2376
2384
  if (!border)
2377
2385
  return {};
@@ -2379,15 +2387,6 @@ function getBorderStyle$3(border) {
2379
2387
  if (border.width && border.style && border.color) {
2380
2388
  style.border = `${border.width} ${border.style} ${border.color}`;
2381
2389
  }
2382
- else {
2383
- const hasIndividual = border.top || border.right || border.bottom || border.left;
2384
- if (hasIndividual) {
2385
- style.borderTop = "none";
2386
- style.borderRight = "none";
2387
- style.borderBottom = "none";
2388
- style.borderLeft = "none";
2389
- }
2390
- }
2391
2390
  if (border.top)
2392
2391
  style.borderTop = `${border.top.width} ${border.top.style} ${border.top.color}`;
2393
2392
  if (border.right)
@@ -2402,143 +2401,114 @@ function getBorderStyleString$1(border) {
2402
2401
  if (!border)
2403
2402
  return "";
2404
2403
  const styles = [];
2404
+ // Standard shorthand
2405
2405
  if (border.width && border.style && border.color) {
2406
- styles.push(`border:${border.width} ${border.style} ${border.color};`);
2406
+ styles.push(`border:${border.width} ${border.style} ${border.color} !important;`);
2407
2407
  }
2408
2408
  else {
2409
- const hasIndividual = border.top || border.right || border.bottom || border.left;
2410
- if (hasIndividual) {
2411
- styles.push("border-top:none;", "border-right:none;", "border-bottom:none;", "border-left:none;");
2412
- }
2409
+ // If desktop had a border and mobile wants "none", we must explicitly kill it
2410
+ styles.push(`border: none !important;`);
2413
2411
  }
2412
+ // Individual sides
2414
2413
  if (border.top)
2415
- styles.push(`border-top:${border.top.width} ${border.top.style} ${border.top.color};`);
2414
+ styles.push(`border-top:${border.top.width} ${border.top.style} ${border.top.color} !important;`);
2416
2415
  if (border.right)
2417
- styles.push(`border-right:${border.right.width} ${border.right.style} ${border.right.color};`);
2416
+ styles.push(`border-right:${border.right.width} ${border.right.style} ${border.right.color} !important;`);
2418
2417
  if (border.bottom)
2419
- styles.push(`border-bottom:${border.bottom.width} ${border.bottom.style} ${border.bottom.color};`);
2418
+ styles.push(`border-bottom:${border.bottom.width} ${border.bottom.style} ${border.bottom.color} !important;`);
2420
2419
  if (border.left)
2421
- styles.push(`border-left:${border.left.width} ${border.left.style} ${border.left.color};`);
2420
+ styles.push(`border-left:${border.left.width} ${border.left.style} ${border.left.color} !important;`);
2422
2421
  return styles.join(" ");
2423
2422
  }
2424
- // ---------------------------------------------------------------------------
2425
- // Merged styles helper — applies mobile overrides on top of desktop values
2426
- // ---------------------------------------------------------------------------
2427
- function mergeConfig(config, overrides) {
2428
- var _a, _b, _c, _d, _e, _f, _g, _h;
2429
- return {
2430
- width: (_a = overrides === null || overrides === void 0 ? void 0 : overrides.width) !== null && _a !== void 0 ? _a : config.width,
2431
- height: (_b = overrides === null || overrides === void 0 ? void 0 : overrides.height) !== null && _b !== void 0 ? _b : config.height,
2432
- maxWidth: (_c = overrides === null || overrides === void 0 ? void 0 : overrides.maxWidth) !== null && _c !== void 0 ? _c : config.maxWidth,
2433
- maxHeight: (_d = overrides === null || overrides === void 0 ? void 0 : overrides.maxHeight) !== null && _d !== void 0 ? _d : config.maxHeight,
2434
- backgroundColor: (_e = overrides === null || overrides === void 0 ? void 0 : overrides.backgroundColor) !== null && _e !== void 0 ? _e : config.backgroundColor,
2435
- padding: (_f = overrides === null || overrides === void 0 ? void 0 : overrides.padding) !== null && _f !== void 0 ? _f : config.padding,
2436
- borderRadius: (_g = overrides === null || overrides === void 0 ? void 0 : overrides.borderRadius) !== null && _g !== void 0 ? _g : config.borderRadius,
2437
- border: (_h = overrides === null || overrides === void 0 ? void 0 : overrides.border) !== null && _h !== void 0 ? _h : config.border,
2438
- };
2439
- }
2440
- // ---------------------------------------------------------------------------
2441
- // Desktop table JSX (same as original)
2442
- // ---------------------------------------------------------------------------
2443
- function renderDesktopTable({ config, className, devNode, devMode, }) {
2444
- const { src, alt, href, target } = config;
2445
- const { width, height, maxWidth, maxHeight, backgroundColor, padding, borderRadius, border, } = mergeConfig(config);
2446
- const borderStyle = getBorderStyle$3(border);
2447
- const imgStyle = Object.assign({ display: "block", objectFit: "cover", width: width || "100%", height: height || "auto", maxWidth: maxWidth || "100%", maxHeight: maxHeight, border: "0", borderRadius: borderRadius }, borderStyle);
2448
- const linkStyle = {
2423
+ function Image({ config, devNode, devMode }) {
2424
+ var _a, _b;
2425
+ const { src, alt, href, target, mobile } = config;
2426
+ const seed = src + (alt || "");
2427
+ const instanceId = seed
2428
+ .split("")
2429
+ .reduce((acc, char) => acc + char.charCodeAt(0), 0)
2430
+ .toString(36);
2431
+ const imgClass = `img-${instanceId}`;
2432
+ // 1. Desktop Dimensional Logic
2433
+ const desktopWidth = config.width || "100%";
2434
+ const isPercent = desktopWidth.includes("%");
2435
+ const widthAttr = desktopWidth.replace("px", "");
2436
+ const heightAttr = (_a = config.height) === null || _a === void 0 ? void 0 : _a.replace("px", "");
2437
+ // Determine the table's "initial" width.
2438
+ // If it's 300px, the table should be 300px, not 100%.
2439
+ const tableWidth = isPercent ? desktopWidth : `${widthAttr}px`;
2440
+ // When width is a percentage, Outlook ignores CSS and renders the image at
2441
+ // its intrinsic pixel size. Setting a concrete `width` HTML attribute gives
2442
+ // Outlook a value to constrain against while modern clients continue to use
2443
+ // the CSS `width: 100%` for fluid rendering.
2444
+ //
2445
+ // If `maxWidth` is a pixel value (e.g. "600px"), we extract the number and
2446
+ // use it as the HTML `width` attribute so Outlook enforces that cap.
2447
+ // Other clients ignore the attribute and rely on CSS styles instead.
2448
+ // If `maxWidth` is not set or is not a pixel value (e.g. "100%"), we fall
2449
+ // back to the original behaviour (numeric string for px widths, undefined
2450
+ // for % widths).
2451
+ const maxWidthPx = ((_b = config.maxWidth) === null || _b === void 0 ? void 0 : _b.endsWith("px"))
2452
+ ? parseInt(config.maxWidth, 10)
2453
+ : undefined;
2454
+ const imgWidthAttr = isPercent ? (maxWidthPx !== null && maxWidthPx !== void 0 ? maxWidthPx : undefined) : widthAttr;
2455
+ // 2. Mobile Overrides (Every property used)
2456
+ let mobileCss = "";
2457
+ if (mobile) {
2458
+ mobileCss = `
2459
+ @media screen and (max-width: 768px) {
2460
+ .wrap-${imgClass} {
2461
+ /* This breaks the px lock from desktop and makes it fluid */
2462
+ width: ${mobile.width || "100%"} !important;
2463
+ max-width: ${mobile.maxWidth || "100%"} !important;
2464
+ min-width: 0 !important;
2465
+ }
2466
+ .td-${imgClass} {
2467
+ padding: ${mobile.padding || "0"} !important;
2468
+ background-color: ${mobile.backgroundColor || "transparent"} !important;
2469
+ width: 100% !important;
2470
+ }
2471
+ .${imgClass} {
2472
+ width: ${mobile.width || "100%"} !important;
2473
+ height: ${mobile.height || "auto"} !important;
2474
+ max-width: ${mobile.maxWidth || "100%"} !important;
2475
+ max-height: ${mobile.maxHeight || "none"} !important;
2476
+ border-radius: ${mobile.borderRadius || "0"} !important;
2477
+ display: ${mobile.hidden ? "none" : "block"} !important;
2478
+ object-fit: ${mobile.objectFit || "fill"} !important;
2479
+ object-position: ${mobile.objectPosition || "center"} !important;
2480
+ ${getBorderStyleString$1(mobile.border)}
2481
+ }
2482
+ }
2483
+ `;
2484
+ }
2485
+ const imgStyle = {
2449
2486
  display: "block",
2450
- textDecoration: "none",
2451
- border: "0",
2487
+ width: isPercent ? "100%" : desktopWidth,
2488
+ height: config.height || "auto",
2489
+ maxWidth: config.maxWidth || "100%",
2490
+ maxHeight: config.maxHeight || "none",
2491
+ borderRadius: config.borderRadius || "0",
2492
+ ...getBorderStyle$3(config.border),
2452
2493
  outline: "none",
2494
+ textDecoration: "none",
2495
+ objectFit: config.objectFit,
2496
+ objectPosition: config.objectPosition,
2453
2497
  };
2454
- const tdStyle = {
2455
- padding: padding,
2456
- backgroundColor: backgroundColor,
2457
- fontSize: "0",
2458
- lineHeight: "0",
2459
- };
2460
- const widthNum = (width === null || width === void 0 ? void 0 : width.endsWith("px")) ? parseInt(width, 10) : undefined;
2461
- const maxWidthNum = (maxWidth === null || maxWidth === void 0 ? void 0 : maxWidth.endsWith("px"))
2462
- ? parseInt(maxWidth, 10)
2463
- : undefined;
2464
- const heightNum = (height === null || height === void 0 ? void 0 : height.endsWith("px")) ? parseInt(height, 10) : undefined;
2465
- const imageElement = (jsx("img", { draggable: false, src: src, alt: alt, style: imgStyle, width: widthNum && maxWidthNum
2466
- ? Math.min(widthNum, maxWidthNum)
2467
- : widthNum || maxWidthNum, height: heightNum, border: 0 }));
2468
- const content = href && !devMode ? (jsx("a", Object.assign({ href: href, target: target, style: linkStyle }, (target === "_blank" ? { rel: "noopener noreferrer" } : {}), { children: imageElement }))) : (imageElement);
2469
- return (jsxs("table", { "aria-label": `Image Wrapper for: ${alt}`, role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, className: className, style: {
2470
- position: "relative",
2471
- width: width || "100%",
2472
- borderCollapse: "collapse",
2473
- }, onClick: devMode ? (e) => e.preventDefault() : undefined, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: tdStyle, align: "center", children: content }) }) }), devMode && !!devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
2474
- }
2475
- // ---------------------------------------------------------------------------
2476
- // Mobile table — HTML string (injected via NonMso, same pattern as Icon VML)
2477
- // ---------------------------------------------------------------------------
2478
- function buildMobileTableHTML({ config, overrides, className, }) {
2479
- const { src, alt, href, target } = config;
2480
- const { width, height, maxWidth, maxHeight, backgroundColor, padding, borderRadius, border, } = mergeConfig(config, overrides);
2481
- const borderStyleStr = getBorderStyleString$1(border);
2482
- const widthNum = (width === null || width === void 0 ? void 0 : width.endsWith("px")) ? parseInt(width, 10) : undefined;
2483
- const maxWidthNum = (maxWidth === null || maxWidth === void 0 ? void 0 : maxWidth.endsWith("px"))
2484
- ? parseInt(maxWidth, 10)
2485
- : undefined;
2486
- const heightNum = (height === null || height === void 0 ? void 0 : height.endsWith("px")) ? parseInt(height, 10) : undefined;
2487
- const resolvedWidth = widthNum && maxWidthNum
2488
- ? Math.min(widthNum, maxWidthNum)
2489
- : widthNum || maxWidthNum;
2490
- const imgTag = `<img
2491
- draggable="false"
2492
- src="${src}"
2493
- alt="${alt}"
2494
- ${resolvedWidth ? `width="${resolvedWidth}"` : ""}
2495
- ${heightNum ? `height="${heightNum}"` : ""}
2496
- border="0"
2497
- style="display:block;object-fit:cover;width:${width || "100%"};height:${height || "auto"};max-width:${maxWidth || "100%"};${maxHeight ? `max-height:${maxHeight};` : ""}border:0;${borderRadius ? `border-radius:${borderRadius};` : ""}${borderStyleStr}"
2498
- />`;
2499
- const content = href
2500
- ? `<a href="${href}" target="${target || "_self"}" style="display:block;text-decoration:none;border:0;outline:none;"${target === "_blank" ? ' rel="noopener noreferrer"' : ""}>${imgTag}</a>`
2501
- : imgTag;
2502
- return `
2503
- <table
2504
- aria-label="Image Wrapper for: ${alt}"
2505
- role="presentation"
2506
- cellpadding="0"
2507
- cellspacing="0"
2508
- border="0"
2509
- class="${className}"
2510
- style="position:relative;width:${width || "100%"};border-collapse:collapse;"
2511
- >
2512
- <tbody>
2513
- <tr>
2514
- <td
2515
- align="center"
2516
- style="padding:${padding || ""};background-color:${backgroundColor || ""};font-size:0;line-height:0;"
2517
- >
2518
- ${content}
2519
- </td>
2520
- </tr>
2521
- </tbody>
2522
- </table>
2523
- `;
2524
- }
2525
- // ---------------------------------------------------------------------------
2526
- // Component
2527
- // ---------------------------------------------------------------------------
2528
- function Image({ config, devNode, devMode }) {
2529
- const { mobile } = config;
2530
- const hasMobileOverrides = !!mobile && !mobile.hidden;
2531
- const isHiddenOnMobile = !!(mobile === null || mobile === void 0 ? void 0 : mobile.hidden);
2532
- return (jsxs(Fragment$1, { children: [renderDesktopTable({
2533
- config,
2534
- className: hasMobileOverrides || isHiddenOnMobile ? "hide-on-mobile" : undefined,
2535
- devNode,
2536
- devMode,
2537
- }), hasMobileOverrides && !devMode && (jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: { width: "100%", borderCollapse: "collapse" }, children: jsx("tbody", { children: jsx("tr", { children: jsx(NonMso, { html: buildMobileTableHTML({
2538
- config,
2539
- overrides: mobile,
2540
- className: "hide-on-desktop",
2541
- }) }) }) }) }))] }));
2498
+ const imageElement = (jsx("img", { src: src, alt: alt, width: imgWidthAttr, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle }));
2499
+ return (jsxs(Fragment$1, { children: [mobile && jsx("style", { dangerouslySetInnerHTML: { __html: mobileCss } }), jsxs("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, className: `wrap-${imgClass}`, align: "center" // Ensures a 300px image stays centered in its parent
2500
+ , style: {
2501
+ width: tableWidth, // Fixed px here prevents the 100% "ghost space"
2502
+ maxWidth: "100%",
2503
+ borderCollapse: "collapse",
2504
+ margin: "0 auto",
2505
+ }, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { className: `td-${imgClass}`, align: "center", style: {
2506
+ padding: config.padding,
2507
+ backgroundColor: config.backgroundColor,
2508
+ fontSize: "0",
2509
+ lineHeight: "0",
2510
+ width: tableWidth, // Lock the cell as well
2511
+ }, children: href && !devMode ? (jsx("a", { href: href, target: target, style: { display: "block", width: "100%" }, children: imageElement })) : (imageElement) }) }) }), devMode && !!devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] })] }));
2542
2512
  }
2543
2513
  var Image_default = memo(Image, arePropsEqual);
2544
2514
 
@@ -2613,35 +2583,54 @@ function Row({ children, config, devNode, devMode }) {
2613
2583
  const numChildren = childrenArray.length;
2614
2584
  const href = getHrefFromInnerLink(config.innerLink);
2615
2585
  const target = (_a = config.innerLink) === null || _a === void 0 ? void 0 : _a.target;
2616
- // 1. Outer TD for Background and Border Radius (no border here).
2617
- // height declared here is the *total* outer height.
2618
- const backgroundTdStyle = Object.assign({ backgroundColor: config.backgroundColor, borderRadius: config.borderRadius, width: config.width || "100%", height: config.height,
2619
- // Background Image styles
2586
+ // 1. Outer TD: Background, Border Radius, Width, Height.
2587
+ const backgroundTdStyle = {
2588
+ backgroundColor: config.backgroundColor,
2589
+ borderRadius: config.borderRadius,
2590
+ width: config.width || "100%",
2591
+ height: config.height,
2620
2592
  backgroundImage: config.backgroundImage
2621
2593
  ? `url(${config.backgroundImage.src})`
2622
- : undefined, backgroundRepeat: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.repeat, backgroundSize: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.size, backgroundPosition: (_d = config.backgroundImage) === null || _d === void 0 ? void 0 : _d.position }, (config.borderRadius && { overflow: "hidden" }));
2623
- // 2. Inner Table for Border and Border Radius.
2624
- // height: 100% so it stretches to fill the outer TD.
2625
- const borderTableStyle = Object.assign({ width: "100%", height: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: config.borderRadius }, getBorderStyle$2(config.border));
2626
- // 3. TD for Padding only — no height.
2627
- // The outer TD owns the total height; setting height here would cause
2628
- // browsers/email clients to treat it as content-box height and add
2629
- // padding on top, making the row taller than the declared height.
2594
+ : undefined,
2595
+ backgroundRepeat: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.repeat,
2596
+ backgroundSize: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.size,
2597
+ backgroundPosition: (_d = config.backgroundImage) === null || _d === void 0 ? void 0 : _d.position,
2598
+ ...(config.borderRadius && { overflow: "hidden" }),
2599
+ };
2600
+ // 2. Inner Table: Border and Border Radius.
2601
+ const borderTableStyle = {
2602
+ width: "100%",
2603
+ height: "100%",
2604
+ borderCollapse: "separate",
2605
+ borderSpacing: 0,
2606
+ borderRadius: config.borderRadius,
2607
+ ...getBorderStyle$2(config.border),
2608
+ };
2609
+ // 3. Padding TD.
2630
2610
  const paddingTdStyle = {
2631
2611
  padding: config.padding,
2632
2612
  width: "100%",
2633
- // height intentionally omitted — padding must be inner, not additive
2634
2613
  verticalAlign: "top",
2635
2614
  };
2636
- // 4. Content Table - horizontal layout
2615
+ // 4. Content Table.
2616
+ //
2617
+ // fillWidth: false/undefined (default) → width: "auto"
2618
+ // Original behavior. Children shrink-wrap to their natural sizes.
2619
+ // Use for icon rows, button rows, social link rows.
2620
+ // Centering works via the Justification Wrapper TD (align + width="100%").
2621
+ //
2622
+ // fillWidth: true → width: "100%"
2623
+ // Content table fills available space, giving Outlook Classic a hard
2624
+ // boundary so text children get a constrained box and line wrapping
2625
+ // triggers correctly. Use for rows containing text + image layouts.
2637
2626
  const contentTableStyle = {
2638
- width: "auto",
2627
+ width: config.fillWidth ? "100%" : "auto",
2639
2628
  height: "100%",
2640
2629
  borderCollapse: "collapse",
2641
2630
  minWidth: "1px",
2642
- maxWidth: config.width || "100%",
2631
+ ...(!config.fillWidth && { maxWidth: config.width || "100%" }),
2643
2632
  };
2644
- // 5. Gap styles for horizontal spacing between children
2633
+ // 5. Gap TD.
2645
2634
  const gapTdStyle = {
2646
2635
  width: config.gap || "0",
2647
2636
  lineHeight: "1px",
@@ -2651,30 +2640,28 @@ function Row({ children, config, devNode, devMode }) {
2651
2640
  ? justifyMap$1[config.justifyContent]
2652
2641
  : "left";
2653
2642
  const tdValign = config.alignItems ? alignMap[config.alignItems] : "top";
2654
- // Content to render - wrapped in anchor if innerLink is defined
2655
- const content = (jsxs("table", Object.assign({ "aria-label": "Row Outer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2643
+ const content = (jsxs("table", { "aria-label": "Row Outer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2656
2644
  position: "relative",
2657
2645
  width: config.width || "100%",
2658
2646
  height: config.height,
2659
2647
  borderCollapse: "collapse",
2660
- } }, (config.height && { height: config.height }), { children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", Object.assign({ style: backgroundTdStyle }, (config.height && { height: config.height }), { children: jsx("table", { "aria-label": "Row Border Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: borderTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: paddingTdStyle, children: jsx("table", { "aria-label": "Row Justification Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2648
+ }, ...(config.height && { height: config.height }), children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: backgroundTdStyle, ...(config.height && { height: config.height }), children: jsx("table", { "aria-label": "Row Border Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: borderTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: paddingTdStyle, children: jsx("table", { "aria-label": "Row Justification Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2661
2649
  width: "100%",
2662
2650
  height: "100%",
2663
2651
  borderCollapse: "collapse",
2664
- }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { align: tdAlign, children: jsx("table", Object.assign({ "aria-label": "Row Content", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: contentTableStyle }, (config.height && { height: config.height }), { className: "content-table row-content-table", "data-mobile-justify": (_e = config.mobile) === null || _e === void 0 ? void 0 : _e.justifyContent, "data-mobile-align": (_f = config.mobile) === null || _f === void 0 ? void 0 : _f.alignItems, "data-mobile-wrap": ((_g = config.mobile) === null || _g === void 0 ? void 0 : _g.wrap) ? "true" : undefined, "data-gap": config.gap, children: jsx("tbody", { children: jsx("tr", { className: "content-tr", children: childrenArray.map((child, index) => (jsxs(Fragment, { children: [jsx("td", { valign: tdValign, style: {
2652
+ }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { align: tdAlign, width: "100%", style: { width: "100%" }, children: jsx("table", { "aria-label": "Row Content", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: contentTableStyle, ...(config.height && { height: config.height }), className: "content-table row-content-table", "data-mobile-justify": (_e = config.mobile) === null || _e === void 0 ? void 0 : _e.justifyContent, "data-mobile-align": (_f = config.mobile) === null || _f === void 0 ? void 0 : _f.alignItems, "data-mobile-wrap": ((_g = config.mobile) === null || _g === void 0 ? void 0 : _g.wrap) ? "true" : undefined, "data-gap": config.gap, children: jsx("tbody", { children: jsx("tr", { className: "content-tr", children: childrenArray.map((child, index) => (jsxs(Fragment, { children: [jsx("td", { valign: tdValign, style: {
2665
2653
  verticalAlign: tdValign,
2666
2654
  textAlign: "left",
2667
2655
  padding: "0",
2668
2656
  margin: "0",
2669
2657
  }, className: "child-cell", children: child }), index < numChildren - 1 &&
2670
- config.gap && (jsx("td", { width: config.gap, style: gapTdStyle, className: "row-gap-td", children: "\u00A0" }, `row-gap-${index}`))] }, `row-child-${index}`))) }) }) })) }) }) }) }) }) }) }) }) })) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] })));
2671
- // Wrap in anchor tag if innerLink is defined and NOT in dev mode
2658
+ config.gap && (jsx("td", { width: config.gap, style: gapTdStyle, className: "row-gap-td", children: "\u00A0" }, `row-gap-${index}`))] }, `row-child-${index}`))) }) }) }) }) }) }) }) }) }) }) }) }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
2672
2659
  if (href && !devMode) {
2673
- return (jsx("a", Object.assign({ href: href }, (target && { target }), { style: {
2660
+ return (jsx("a", { href: href, ...(target && { target }), style: {
2674
2661
  textDecoration: "none",
2675
2662
  color: "inherit",
2676
2663
  display: "block",
2677
- }, children: content })));
2664
+ }, children: content }));
2678
2665
  }
2679
2666
  return content;
2680
2667
  }
@@ -2718,9 +2705,18 @@ function getBorderStyle$1(border) {
2718
2705
  const Section = ({ config, children, devNode, }) => {
2719
2706
  var _a, _b, _c;
2720
2707
  const { sectionType, padding } = config;
2721
- return (jsxs("table", { "aria-label": `Section |Table | ${sectionType}`, role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: Object.assign(Object.assign({ position: "relative", width: "100%", backgroundColor: config.backgroundColor }, getBorderStyle$1(config.border)), { backgroundImage: config.backgroundImage
2708
+ return (jsxs("table", { "aria-label": `Section |Table | ${sectionType}`, role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2709
+ position: "relative",
2710
+ width: "100%",
2711
+ backgroundColor: config.backgroundColor,
2712
+ ...getBorderStyle$1(config.border),
2713
+ backgroundImage: config.backgroundImage
2722
2714
  ? `url(${config.backgroundImage.src})`
2723
- : undefined, backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat, backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size, backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position }), children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: {
2715
+ : undefined,
2716
+ backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat,
2717
+ backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size,
2718
+ backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position,
2719
+ }, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: {
2724
2720
  padding: padding,
2725
2721
  }, children: children }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsxs("td", { children: [jsxs("span", { style: {
2726
2722
  backgroundColor: "black",
@@ -2761,16 +2757,19 @@ function Spacer({ config, devNode }) {
2761
2757
  const spacerHeightAttribute = parseInt(height, 10) || 1;
2762
2758
  return (
2763
2759
  // Outer table ensures the spacer spans the full width of its container
2764
- jsxs("table", Object.assign({ "aria-label": "Vertical Spacer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: Object.assign({
2760
+ jsxs("table", { "aria-label": "Vertical Spacer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2765
2761
  // --- Start dev
2766
- position: "relative" }, spacerTableStyle) }, { height: spacerHeightAttribute }, { className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: spacerTdStyle,
2762
+ position: "relative",
2763
+ // --- End dev
2764
+ ...spacerTableStyle,
2765
+ }, ...{ height: spacerHeightAttribute }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: spacerTdStyle,
2767
2766
  // Explicit height attribute
2768
- height: spacerHeightAttribute, children: "\u00A0" }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] })));
2767
+ height: spacerHeightAttribute, children: "\u00A0" }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
2769
2768
  }
2770
2769
  var Spacer_default = memo(Spacer, arePropsEqual);
2771
2770
 
2772
2771
  function Text({ config, devMode, children }) {
2773
- const { text, padding, color, textAlign, fontFamily, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, backgroundColor, opacity, whiteSpace, wordBreak = "break-all", } = config;
2772
+ const { text, padding, color, textAlign, fontFamily, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, backgroundColor, opacity, whiteSpace, wordBreak = "break-all", maxWidth } = config;
2774
2773
  // 1. TD Style: Where padding and background are reliably applied.
2775
2774
  const tdStyle = {
2776
2775
  padding: padding,
@@ -2797,6 +2796,7 @@ function Text({ config, devMode, children }) {
2797
2796
  wordBreak: wordBreak,
2798
2797
  margin: "0",
2799
2798
  padding: "0",
2799
+ maxWidth
2800
2800
  };
2801
2801
  // Determine content to render
2802
2802
  const content = text !== null && text !== void 0 ? text : children;
@@ -2980,7 +2980,13 @@ function Icon({ config, devNode, devMode, children }) {
2980
2980
  lineHeight: "0",
2981
2981
  };
2982
2982
  // 4. Inner Table Style: Apply border here with border-collapse: separate
2983
- const innerTableStyle = Object.assign({ width: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: borderRadius }, borderStyle);
2983
+ const innerTableStyle = {
2984
+ width: "100%",
2985
+ borderCollapse: "separate",
2986
+ borderSpacing: 0,
2987
+ borderRadius: borderRadius,
2988
+ ...borderStyle,
2989
+ };
2984
2990
  // 5. Inner TD Style: Padding
2985
2991
  const innerTdStyle = {
2986
2992
  padding: padding,
@@ -3028,7 +3034,7 @@ function Icon({ config, devNode, devMode, children }) {
3028
3034
  // Icon image element
3029
3035
  const iconElement = devMode && !!children ? (children) : iconSrc ? (jsx("img", { draggable: false, src: iconSrc, alt: "", style: imgStyle, width: widthNum, height: heightNum, border: 0 })) : (jsx(Fragment$1, {}));
3030
3036
  // Wrap in link if href exists and not in dev mode
3031
- const content = href && !devMode ? (jsx("a", Object.assign({ href: href, target: target, style: linkStyle }, (target === "_blank" ? { rel: "noopener noreferrer" } : {}), { children: iconElement }))) : (iconElement);
3037
+ const content = href && !devMode ? (jsx("a", { href: href, target: target, style: linkStyle, ...(target === "_blank" ? { rel: "noopener noreferrer" } : {}), children: iconElement })) : (iconElement);
3032
3038
  // Build the HTML content with VML support (only when NOT in dev mode)
3033
3039
  const useVML = !devMode && backgroundColor && numericBorderRadius > 0;
3034
3040
  const htmlContent = useVML
@@ -3076,5 +3082,24 @@ function Icon({ config, devNode, devMode, children }) {
3076
3082
  }
3077
3083
  var Icon_default = memo(Icon, arePropsEqual);
3078
3084
 
3085
+ /**
3086
+ * Content rendered by Outlook Classic only.
3087
+ * Outputs: <!--[if mso]> ... <![endif]-->
3088
+ */
3089
+ function MsoOnly({ html }) {
3090
+ return (jsx("td", { dangerouslySetInnerHTML: {
3091
+ __html: `<!--[if mso]>${html}<![endif]-->`,
3092
+ } }));
3093
+ }
3094
+ /**
3095
+ * Content hidden from Outlook Classic, visible in all other clients.
3096
+ * Outputs: <!--[if !mso]><!--> ... <!--<![endif]-->
3097
+ */
3098
+ function NonMso({ html }) {
3099
+ return (jsx("td", { dangerouslySetInnerHTML: {
3100
+ __html: `<!--[if !mso]><!-->${html}<!--<![endif]-->`,
3101
+ } }));
3102
+ }
3103
+
3079
3104
  export { Body, Button_default as Button, Column_default as Column, Container_default as Container, Divider_default as Divider, Font, Head, Heading_default as Heading, Html, Icon_default as Icon, Image_default as Image, MsoOnly, NonMso, Row_default as Row, Section_default as Section, Spacer_default as Spacer, Text_default as Text };
3080
3105
  //# sourceMappingURL=index.esm.js.map