@pagenflow/email 1.4.2 → 1.4.4

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
@@ -15,24 +15,42 @@ function Body({ children, config = {} }) {
15
15
  const bgSize = ((_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.size) || "cover";
16
16
  const bgPosition = ((_d = config.backgroundImage) === null || _d === void 0 ? void 0 : _d.position) || "center";
17
17
  // 1. Style for the <body> tag inline
18
- const bodyStyle = Object.assign({ backgroundColor: globalBackgroundColor, color: globalColor, fontSize: globalFontSize, lineHeight: globalLineHeight, padding: "0", margin: "0", WebkitTextSizeAdjust: "100%", overflowX: "hidden", ["msTextSizeAdjust"]: "100%", ["msoLineHeightRule"]: "exactly", fontFamily: globalFontFamily }, (bgImage && {
19
- backgroundImage: `url(${bgImage})`,
20
- backgroundRepeat: bgRepeat,
21
- backgroundSize: bgSize,
22
- backgroundPosition: bgPosition,
23
- }));
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
+ };
24
38
  // 2. Style for the top-level <table> wrapper
25
39
  const outerTableStyle = {
26
40
  width: "100%",
27
41
  ["msoLineHeightRule"]: "exactly",
28
42
  borderCollapse: "collapse",
29
43
  };
30
- return (jsxs("body", { style: bodyStyle, children: [jsx("center", { style: Object.assign({ width: "100%", background: globalBackgroundColor }, (bgImage && {
31
- backgroundImage: `url(${bgImage})`,
32
- backgroundRepeat: bgRepeat,
33
- backgroundSize: bgSize,
34
- backgroundPosition: bgPosition,
35
- })), 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: {
36
54
  display: "none",
37
55
  whiteSpace: "nowrap",
38
56
  font: "15px courier",
@@ -1519,40 +1537,26 @@ const justifyMap$3 = {
1519
1537
  center: "center",
1520
1538
  end: "right",
1521
1539
  };
1522
- function getBorderStyle$6(border) {
1523
- if (!border)
1524
- return {};
1525
- const style = {};
1526
- // If a full border is specified, apply it
1527
- if (border.width && border.style && border.color) {
1528
- style.border = `${border.width} ${border.style} ${border.color}`;
1529
- }
1530
- else {
1531
- // If only individual borders are specified, explicitly set others to 'none'
1532
- // to prevent Outlook Classic from showing black borders
1533
- const hasIndividualBorders = border.top || border.right || border.bottom || border.left;
1534
- if (hasIndividualBorders) {
1535
- // Default all borders to none
1536
- style.borderTop = "none";
1537
- style.borderRight = "none";
1538
- style.borderBottom = "none";
1539
- style.borderLeft = "none";
1540
- }
1541
- }
1542
- // Override with specific borders if provided
1543
- if (border.top) {
1544
- style.borderTop = `${border.top.width} ${border.top.style} ${border.top.color}`;
1545
- }
1546
- if (border.right) {
1547
- style.borderRight = `${border.right.width} ${border.right.style} ${border.right.color}`;
1548
- }
1549
- if (border.bottom) {
1550
- style.borderBottom = `${border.bottom.width} ${border.bottom.style} ${border.bottom.color}`;
1551
- }
1552
- if (border.left) {
1553
- style.borderLeft = `${border.left.width} ${border.left.style} ${border.left.color}`;
1540
+ // Helper to build link href based on innerLink type (mirrors Icon component)
1541
+ function buildLinkHref$2(innerLink) {
1542
+ if (!innerLink || innerLink.type === "none")
1543
+ return null;
1544
+ switch (innerLink.type) {
1545
+ case "url":
1546
+ return innerLink.url || null;
1547
+ case "email":
1548
+ return innerLink.email ? `mailto:${innerLink.email}` : null;
1549
+ case "phone":
1550
+ return innerLink.phone ? `tel:${innerLink.phone}` : null;
1551
+ case "anchor":
1552
+ return innerLink.anchor ? `#${innerLink.anchor}` : null;
1553
+ case "page_top":
1554
+ return "#top";
1555
+ case "page_bottom":
1556
+ return "#bottom";
1557
+ default:
1558
+ return null;
1554
1559
  }
1555
- return style;
1556
1560
  }
1557
1561
  function getBorderStyleString$2(border) {
1558
1562
  if (!border)
@@ -1589,38 +1593,48 @@ function getBorderStyleString$2(border) {
1589
1593
  return styles.join(" ");
1590
1594
  }
1591
1595
  function Button({ config, devMode }) {
1592
- const { href, children, backgroundColor = "#007bff", // Default blue
1593
- color = "#ffffff", padding = "12px 24px", borderRadius = "3px", border, width, maxWidth, justifyContent = "center", textAlign = "center", fontSize = "16px", fontWeight = "500", fontStyle, fontFamily = "Arial, sans-serif", lineHeight = "1.2", letterSpacing, textTransform, textDecoration = "none", direction, verticalAlign, opacity, whiteSpace = "normal", wordBreak = "break-word", } = config;
1596
+ const { innerLink, children, backgroundColor, color, padding, borderRadius, border, width, maxWidth, justifyContent, textAlign, fontSize, fontWeight, fontStyle, fontFamily, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, opacity, whiteSpace, wordBreak, } = config;
1597
+ // Resolve href from innerLink
1598
+ const href = buildLinkHref$2(innerLink);
1599
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
1594
1600
  // Sanitize fontFamily early so safeFontFamily is available for all paths below.
1595
1601
  const safeFontFamily = fontFamily
1596
1602
  ? fontFamily.replace(/['"]/g, "")
1597
1603
  : fontFamily;
1598
- // 2. Outer TD Style for Background and Border Radius (no border)
1599
- const backgroundTdStyle = Object.assign(Object.assign({ backgroundColor: backgroundColor, borderRadius: borderRadius, width: width || "auto" }, (maxWidth && { maxWidth: maxWidth })), (borderRadius && { overflow: "hidden" }));
1600
- // 3. Border styles
1601
- getBorderStyle$6(border);
1604
+ // Outer TD Style for Background and Border Radius (no border)
1605
+ const backgroundTdStyle = {
1606
+ backgroundColor: backgroundColor,
1607
+ borderRadius: borderRadius,
1608
+ width: width || "auto",
1609
+ ...(maxWidth && { maxWidth: maxWidth }),
1610
+ // Overflow hidden to clip background to border-radius
1611
+ ...(borderRadius && { overflow: "hidden" }),
1612
+ };
1613
+ // Border styles
1602
1614
  const borderStyleString = getBorderStyleString$2(border);
1603
1615
  // --- Determine Button Approach Based on Width ---
1604
1616
  // Check if width is percentage-based or not defined
1605
1617
  const isPercentageWidth = !width || width.includes("%");
1606
1618
  const useSimpleOutlookApproach = isPercentageWidth;
1607
- const align = justifyMap$3[justifyContent];
1619
+ const align = justifyContent ? justifyMap$3[justifyContent] : undefined;
1608
1620
  // --- VML Calculation and Code for Outlook Compatibility (Fixed Width Only) ---
1609
1621
  let vmlButton = "";
1610
1622
  if (!useSimpleOutlookApproach) {
1611
1623
  // VML needs fixed pixel height. We estimate it based on padding and potential wrapping.
1612
- const numericPadding = parseInt(padding.split(" ")[0] || "12", 10);
1613
- const numericFontSize = parseInt(fontSize, 10);
1614
- const numericLineHeight = lineHeight.includes("px")
1615
- ? parseInt(lineHeight, 10)
1616
- : numericFontSize * parseFloat(lineHeight);
1624
+ const numericPadding = padding ? parseInt(padding.split(" ")[0] || "12", 10) : 12;
1625
+ const numericFontSize = fontSize ? parseInt(fontSize, 10) : 0;
1626
+ const numericLineHeight = lineHeight
1627
+ ? lineHeight.includes("px")
1628
+ ? parseInt(lineHeight, 10)
1629
+ : numericFontSize * parseFloat(lineHeight)
1630
+ : numericFontSize;
1617
1631
  // Trust user's explicit pixel width - no calculation needed
1618
1632
  const vmlWidth = parseInt(width, 10);
1619
1633
  // Calculate VML height - trust user's padding and let text wrap naturally
1620
1634
  // VML v:textbox will handle text wrapping automatically
1621
1635
  const textContent = typeof children === "string" ? children : "";
1622
1636
  // Estimate number of lines based on text length and button width
1623
- const horizontalPadding = padding.split(" ")[1]
1637
+ const horizontalPadding = (padding === null || padding === void 0 ? void 0 : padding.split(" ")[1])
1624
1638
  ? parseInt(padding.split(" ")[1], 10) * 2
1625
1639
  : numericPadding * 2;
1626
1640
  const availableTextWidth = vmlWidth - horizontalPadding;
@@ -1633,15 +1647,17 @@ function Button({ config, devMode }) {
1633
1647
  // Add extra 4px buffer to prevent bottom cropping in VML
1634
1648
  const vmlHeight = Math.max(numericPadding * 2 + textHeight + 4, 40);
1635
1649
  // VML colors must use the full hex format (e.g., #000000)
1636
- const vmlFillColor = backgroundColor.startsWith("#")
1637
- ? backgroundColor
1638
- : `#${backgroundColor}`;
1650
+ const vmlFillColor = backgroundColor
1651
+ ? backgroundColor.startsWith("#")
1652
+ ? backgroundColor
1653
+ : `#${backgroundColor}`
1654
+ : undefined;
1639
1655
  // VML stroke color for border
1640
1656
  const vmlStrokeColor = (border === null || border === void 0 ? void 0 : border.color) || vmlFillColor;
1641
1657
  const vmlStrokeWeight = (border === null || border === void 0 ? void 0 : border.width) ? parseInt(border.width, 10) : 0;
1642
1658
  const hasVmlStroke = vmlStrokeWeight > 0;
1643
1659
  // Build VML font styles - consistent with other rendering paths
1644
- const vmlFontWeight = fontWeight || "500";
1660
+ const vmlFontWeight = fontWeight;
1645
1661
  const vmlFontStyle = fontStyle === "italic" ? "font-style:italic;" : "";
1646
1662
  const vmlLetterSpacing = letterSpacing
1647
1663
  ? `letter-spacing:${letterSpacing};`
@@ -1652,12 +1668,12 @@ function Button({ config, devMode }) {
1652
1668
  const vmlTextDecoration = textDecoration && textDecoration !== "none"
1653
1669
  ? `text-decoration:${textDecoration};`
1654
1670
  : "";
1655
- const vmlWhiteSpace = whiteSpace !== "normal" ? `white-space:${whiteSpace};` : "";
1671
+ const vmlWhiteSpace = whiteSpace ? `white-space:${whiteSpace};` : "";
1656
1672
  const vmlDirection = direction ? `direction:${direction};` : "";
1657
1673
  const vmlOpacity = opacity !== undefined ? `opacity:${opacity};` : "";
1658
1674
  // VML code uses MSO conditional comments to render only in Outlook
1659
1675
  // Use table with explicit MSO height for vertical centering
1660
- const horizontalPaddingValue = padding.split(" ")[1]
1676
+ const horizontalPaddingValue = (padding === null || padding === void 0 ? void 0 : padding.split(" ")[1])
1661
1677
  ? parseInt(padding.split(" ")[1], 10)
1662
1678
  : numericPadding;
1663
1679
  // For VML, we need to use a table inside to properly apply padding and centering
@@ -1666,7 +1682,7 @@ function Button({ config, devMode }) {
1666
1682
  if (textAlign === "center") {
1667
1683
  vmlAlignAttr = 'align="center"';
1668
1684
  }
1669
- else {
1685
+ else if (textAlign) {
1670
1686
  vmlAlignStyle = `text-align:${textAlign};`;
1671
1687
  }
1672
1688
  // Border radius is intentionally omitted (arcsize="0%") for Outlook Classic.
@@ -1674,12 +1690,12 @@ function Button({ config, devMode }) {
1674
1690
  // is inconsistent, so we render sharp corners there instead.
1675
1691
  vmlButton = `
1676
1692
  <!--[if mso]>
1677
- <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}">
1693
+ <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" ${href ? `href="${href}"` : ""} style="height:${vmlHeight}px;width:${vmlWidth}px;" arcsize="0%" ${vmlStrokeColor ? `strokecolor="${vmlStrokeColor}"` : ""} ${hasVmlStroke ? `strokeweight="${vmlStrokeWeight}px"` : 'stroke="f"'} ${vmlFillColor ? `fillcolor="${vmlFillColor}"` : ""}>
1678
1694
  <w:anchorlock/>
1679
1695
  <v:textbox inset="${horizontalPaddingValue}px,${numericPadding}px,${horizontalPaddingValue}px,${numericPadding}px">
1680
1696
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse:collapse;">
1681
1697
  <tr>
1682
- <td ${vmlAlignAttr} valign="middle" style="${vmlAlignStyle}color:${color};font-family:${safeFontFamily};font-size:${fontSize};font-weight:${vmlFontWeight};${vmlFontStyle}${vmlLetterSpacing}${vmlTextTransform}${vmlTextDecoration}${vmlWhiteSpace}${vmlDirection}${vmlOpacity}line-height:${lineHeight};mso-line-height-rule:exactly;">
1698
+ <td ${vmlAlignAttr} valign="middle" style="${vmlAlignStyle}${color ? `color:${color};` : ""}${safeFontFamily ? `font-family:${safeFontFamily};` : ""}${fontSize ? `font-size:${fontSize};` : ""}${vmlFontWeight ? `font-weight:${vmlFontWeight};` : ""}${vmlFontStyle}${vmlLetterSpacing}${vmlTextTransform}${vmlTextDecoration}${vmlWhiteSpace}${vmlDirection}${vmlOpacity}${lineHeight ? `line-height:${lineHeight};` : ""}mso-line-height-rule:exactly;">
1683
1699
  ${typeof children === "string" ? children : ""}
1684
1700
  </td>
1685
1701
  </tr>
@@ -1703,10 +1719,10 @@ function Button({ config, devMode }) {
1703
1719
  const textTransformProp = textTransform
1704
1720
  ? `text-transform: ${textTransform};`
1705
1721
  : "";
1706
- const whiteSpaceProp = whiteSpace !== "normal" ? `white-space: ${whiteSpace};` : "";
1722
+ const whiteSpaceProp = whiteSpace ? `white-space: ${whiteSpace};` : "";
1707
1723
  const directionProp = direction ? `direction: ${direction};` : "";
1708
1724
  const opacityProp = opacity !== undefined ? `opacity: ${opacity};` : "";
1709
- const wordBreakProp = wordBreak !== "break-word" ? `word-break: ${wordBreak};` : "";
1725
+ const wordBreakProp = wordBreak ? `word-break: ${wordBreak};` : "";
1710
1726
  // Border radius is intentionally omitted from the Outlook Classic table cell.
1711
1727
  // Outlook Classic ignores border-radius on table cells anyway, and including it
1712
1728
  // can cause unexpected rendering artifacts, so we explicitly leave it out.
@@ -1714,13 +1730,17 @@ function Button({ config, devMode }) {
1714
1730
  <!--[if mso]>
1715
1731
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse;">
1716
1732
  <tr>
1717
- <td align="${align}" style="padding: 0;">
1733
+ <td ${align ? `align="${align}"` : ""} style="padding: 0;">
1718
1734
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="${width || "auto"}" style="border-collapse: collapse;">
1719
1735
  <tr>
1720
- <td bgcolor="${backgroundColor}" align="${textAlign}" style="padding: ${padding}; text-align: ${textAlign}; ${borderStyleString}">
1721
- <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;">
1722
- ${typeof children === "string" ? children : ""}
1723
- </a>
1736
+ <td ${backgroundColor ? `bgcolor="${backgroundColor}"` : ""} ${textAlign ? `align="${textAlign}"` : ""} style="${padding ? `padding: ${padding};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${borderStyleString}">
1737
+ ${href
1738
+ ? `<a href="${href}" target="${target}" rel="noopener noreferrer" style="${color ? `color: ${color};` : ""} ${textDecorationStyle} display: block; ${safeFontFamily ? `font-family: ${safeFontFamily};` : ""} ${fontSize ? `font-size: ${fontSize};` : ""} ${fontWeight ? `font-weight: ${fontWeight};` : ""} ${fontStyleProp} ${lineHeight ? `line-height: ${lineHeight};` : ""} ${letterSpacingProp} ${textTransformProp} ${textAlign ? `text-align: ${textAlign};` : ""} ${whiteSpaceProp} ${directionProp} ${opacityProp} ${wordBreakProp} mso-line-height-rule: exactly;">
1739
+ ${typeof children === "string" ? children : ""}
1740
+ </a>`
1741
+ : `<span style="${color ? `color: ${color};` : ""} ${textDecorationStyle} display: block; ${safeFontFamily ? `font-family: ${safeFontFamily};` : ""} ${fontSize ? `font-size: ${fontSize};` : ""} ${fontWeight ? `font-weight: ${fontWeight};` : ""} ${fontStyleProp} ${lineHeight ? `line-height: ${lineHeight};` : ""} ${letterSpacingProp} ${textTransformProp} ${textAlign ? `text-align: ${textAlign};` : ""} ${whiteSpaceProp} ${directionProp} ${opacityProp} ${wordBreakProp} mso-line-height-rule: exactly;">
1742
+ ${typeof children === "string" ? children : ""}
1743
+ </span>`}
1724
1744
  </td>
1725
1745
  </tr>
1726
1746
  </table>
@@ -1734,7 +1754,7 @@ function Button({ config, devMode }) {
1734
1754
  // fontFamily uses the sanitized value so embedded quotes never break the
1735
1755
  // style attribute string (which is always wrapped in double quotes).
1736
1756
  const sharedTextStyles = [
1737
- `color: ${color};`,
1757
+ color ? `color: ${color};` : "",
1738
1758
  safeFontFamily ? `font-family: ${safeFontFamily};` : "",
1739
1759
  fontSize ? `font-size: ${fontSize};` : "",
1740
1760
  fontWeight ? `font-weight: ${fontWeight};` : "",
@@ -1747,7 +1767,7 @@ function Button({ config, devMode }) {
1747
1767
  : "",
1748
1768
  direction ? `direction: ${direction};` : "",
1749
1769
  opacity !== undefined ? `opacity: ${opacity};` : "",
1750
- whiteSpace !== "normal" ? `white-space: ${whiteSpace};` : "",
1770
+ whiteSpace ? `white-space: ${whiteSpace};` : "",
1751
1771
  ]
1752
1772
  .filter(Boolean)
1753
1773
  .join(" ");
@@ -1762,33 +1782,43 @@ function Button({ config, devMode }) {
1762
1782
  padding: 0,
1763
1783
  }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { align: align, style: {
1764
1784
  padding: 0,
1765
- }, children: jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: Object.assign(Object.assign({
1785
+ }, children: jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
1766
1786
  // --- Start dev
1767
- position: "relative",
1787
+ position: "relative",
1768
1788
  // --- End dev
1769
- width: width || "auto" }, (maxWidth && { maxWidth: maxWidth })), { borderCollapse: "collapse",
1789
+ width: width || "auto",
1790
+ ...(maxWidth && { maxWidth: maxWidth }),
1791
+ borderCollapse: "collapse",
1770
1792
  // base
1771
- 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: {
1793
+ boxSizing: "border-box",
1794
+ border: 0,
1795
+ margin: 0,
1796
+ padding: 0,
1797
+ }, onClick: devMode ? (e) => e.preventDefault() : undefined, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { dangerouslySetInnerHTML: {
1772
1798
  __html: `
1773
- ${devMode ? "" : useSimpleOutlookApproach ? simpleOutlookButton : vmlButton}
1799
+ ${useSimpleOutlookApproach ? simpleOutlookButton : vmlButton}
1774
1800
  <!--[if !mso]><!-->
1775
1801
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse; width: 100%;">
1776
1802
  <tbody>
1777
1803
  <tr>
1778
- <td style="background-color: ${backgroundTdStyle.backgroundColor}; border-radius: ${backgroundTdStyle.borderRadius}; width: ${backgroundTdStyle.width}; ${maxWidth ? `max-width: ${maxWidth};` : ""} ${borderRadius ? "overflow: hidden;" : ""}">
1779
- <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: separate; border-spacing: 0; border-radius: ${borderRadius}; width: 100%; ${borderStyleString}">
1804
+ <td style="${backgroundTdStyle.backgroundColor ? `background-color: ${backgroundTdStyle.backgroundColor};` : ""} ${backgroundTdStyle.borderRadius ? `border-radius: ${backgroundTdStyle.borderRadius};` : ""} width: ${backgroundTdStyle.width}; ${maxWidth ? `max-width: ${maxWidth};` : ""} ${borderRadius ? "overflow: hidden;" : ""}">
1805
+ <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: separate; border-spacing: 0; ${borderRadius ? `border-radius: ${borderRadius};` : ""} width: 100%; ${borderStyleString}">
1780
1806
  <tbody>
1781
1807
  <tr>
1782
1808
  <td style="padding: 0;">
1783
1809
  ${devMode
1784
- ? `<span style="${sharedTextStyles} display: block; text-align: ${textAlign}; word-break: ${wordBreak}; padding: ${padding};">
1810
+ ? `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; ${wordBreak ? `word-break: ${wordBreak};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${padding ? `padding: ${padding};` : ""}">
1785
1811
  ${typeof children === "string" ? children : ""}
1786
1812
  </span>`
1787
- : `<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};">
1788
- <span style="${sharedTextStyles}">
1813
+ : href
1814
+ ? `<a href="${href}" target="${target}" rel="noopener noreferrer" style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; ${wordBreak ? `word-break: ${wordBreak};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${padding ? `padding: ${padding};` : ""}">
1815
+ <span>
1816
+ ${typeof children === "string" ? children : ""}
1817
+ </span>
1818
+ </a>`
1819
+ : `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; ${wordBreak ? `word-break: ${wordBreak};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${padding ? `padding: ${padding};` : ""}">
1789
1820
  ${typeof children === "string" ? children : ""}
1790
- </span>
1791
- </a>`}
1821
+ </span>`}
1792
1822
  </td>
1793
1823
  </tr>
1794
1824
  </tbody>
@@ -1865,14 +1895,31 @@ function Column({ children, config, devNode }) {
1865
1895
  };
1866
1896
  // 2. Outer TD style: Background and Border Radius (no border here).
1867
1897
  // height is set so the TD occupies the full declared height.
1868
- const outerTdStyle = Object.assign({ width: config.width, height: config.height, backgroundColor: config.backgroundColor, borderRadius: config.borderRadius,
1898
+ const outerTdStyle = {
1899
+ width: config.width,
1900
+ height: config.height,
1901
+ backgroundColor: config.backgroundColor,
1902
+ borderRadius: config.borderRadius,
1869
1903
  // Background Image styles
1870
1904
  backgroundImage: config.backgroundImage
1871
1905
  ? `url(${config.backgroundImage.src})`
1872
- : 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" }));
1906
+ : undefined,
1907
+ backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat,
1908
+ backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size,
1909
+ backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position,
1910
+ // Overflow hidden to clip background to border-radius
1911
+ ...(config.borderRadius && { overflow: "hidden" }),
1912
+ };
1873
1913
  // 2b. Inner table style: Border and Border Radius.
1874
1914
  // height: 100% so it stretches to fill the outer TD's declared height.
1875
- const innerTableStyle = Object.assign({ width: "100%", height: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: config.borderRadius }, getBorderStyle$5(config.border));
1915
+ const innerTableStyle = {
1916
+ width: "100%",
1917
+ height: "100%", // fill the outer TD rather than re-declaring the pixel value
1918
+ borderCollapse: "separate",
1919
+ borderSpacing: 0,
1920
+ borderRadius: config.borderRadius,
1921
+ ...getBorderStyle$5(config.border),
1922
+ };
1876
1923
  // 3. Inner TD style: Padding and Vertical Alignment only.
1877
1924
  // *** No height here. ***
1878
1925
  // The outer TD/table owns the height; padding is purely inner spacing,
@@ -1904,7 +1951,10 @@ function Column({ children, config, devNode }) {
1904
1951
  : "top", align: config.alignItems
1905
1952
  ? alignMap$2[config.alignItems]
1906
1953
  : "left", children: child }) }), index < numChildren - 1 && (jsx("tr", { children: jsx("td", { style: gapSpacerStyle, children: "\u00A0" }) }))] }, `col-child-${index}`))) }) })) : (children) }) }) }) }));
1907
- 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 }) }) }))] })));
1954
+ return (jsxs("table", { "aria-label": "Column Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
1955
+ position: "relative",
1956
+ ...outerTableStyle,
1957
+ }, ...(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 }) }) }))] }));
1908
1958
  }
1909
1959
  var Column_default = memo(Column, arePropsEqual);
1910
1960
 
@@ -2009,11 +2059,27 @@ function Container({ children, config, devMode, devNode }) {
2009
2059
  borderCollapse: "collapse",
2010
2060
  };
2011
2061
  // 1. Background TD Style - Background color, border radius, background image
2012
- const backgroundTdStyle = Object.assign({ backgroundColor: config.backgroundColor, borderRadius: config.borderRadius, maxWidth: widthType === "fixed" ? config.width || "600px" : undefined, backgroundImage: config.backgroundImage
2062
+ const backgroundTdStyle = {
2063
+ backgroundColor: config.backgroundColor,
2064
+ borderRadius: config.borderRadius,
2065
+ maxWidth: widthType === "fixed" ? config.width || "600px" : undefined,
2066
+ backgroundImage: config.backgroundImage
2013
2067
  ? `url(${config.backgroundImage.src})`
2014
- : 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" }));
2068
+ : undefined,
2069
+ backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat,
2070
+ backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size,
2071
+ backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position,
2072
+ // Overflow hidden to clip background to border-radius
2073
+ ...(config.borderRadius && { overflow: "hidden" }),
2074
+ };
2015
2075
  // 2. Border Table Style - Border and border radius
2016
- const borderTableStyle = Object.assign({ width: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: config.borderRadius }, getBorderStyle$4(config.border));
2076
+ const borderTableStyle = {
2077
+ width: "100%",
2078
+ borderCollapse: "separate",
2079
+ borderSpacing: 0,
2080
+ borderRadius: config.borderRadius,
2081
+ ...getBorderStyle$4(config.border),
2082
+ };
2017
2083
  // 3. Padding TD Style
2018
2084
  const innerTdStyle = {
2019
2085
  padding: config.padding,
@@ -2053,7 +2119,10 @@ function Container({ children, config, devMode, devNode }) {
2053
2119
  }
2054
2120
  return (jsx("td", { className: isStacking ? "stack-td" : undefined, width: getChildWidths[index], style: childTdStyle, children: child }, `child-${index}`));
2055
2121
  });
2056
- 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: [
2122
+ return (jsx("table", { "aria-label": `Container | Table Outer`, cellPadding: 0, cellSpacing: 0, role: "presentation", border: 0, style: {
2123
+ position: "relative",
2124
+ ...outerTableStyle,
2125
+ }, children: jsx("tbody", { children: jsx("tr", { children: jsxs("td", { align: justifyAlign, children: [jsx("div", { dangerouslySetInnerHTML: { __html: msoFixedWrapper } }), jsxs("table", { className: [
2057
2126
  widthType === "fixed" ? "container-fixed-width" : undefined,
2058
2127
  devMode ? "main-wrapper relative" : undefined,
2059
2128
  ]
@@ -2068,40 +2137,48 @@ var Container_default = memo(Container, arePropsEqual);
2068
2137
 
2069
2138
  function Divider({ config, devNode }) {
2070
2139
  const { height = "1px", color = "#cccccc", width = "100%", margin = "20px 0", align = "center", hideOnMobile, } = config;
2071
- // 1. Outer TD Style: Applies the vertical spacing (margin)
2072
- const outerTdStyle = {
2073
- padding: margin,
2074
- fontSize: "0",
2075
- lineHeight: "0",
2076
- width: "100%",
2077
- };
2078
- // 2. Divider Table Style: Applies the line properties
2079
- const dividerTableStyle = {
2080
- width: width,
2081
- height: height,
2082
- backgroundColor: color,
2083
- borderCollapse: "collapse",
2084
- border: "0",
2085
- // ✅ FIX 1: Use string literal indexing for MSO properties
2086
- // ["mso-table-lspace" as string]: "0pt",
2087
- ["msoTableLspace"]: "0pt",
2088
- // ["mso-table-rspace" as string]: "0pt",
2089
- ["msoTableRspace"]: "0pt",
2090
- };
2091
- // Parse height for the HTML attribute
2092
- const dividerHeightAttribute = parseInt(height, 10) || 1;
2093
- return (jsxs("table", { "aria-label": "Divider Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2094
- // --- Start dev
2095
- position: "relative",
2096
- // --- End dev
2140
+ const heightPx = parseInt(height, 10) || 1;
2141
+ // Parse margin into paddingTop / paddingBottom for the outer TD.
2142
+ // Outlook ignores shorthand "20px 0" on TDs — must be explicit.
2143
+ const [marginTopRaw = "0", marginRightRaw = "0", marginBottomRaw, marginLeftRaw] = margin.trim().split(/\s+/);
2144
+ const marginTop = marginTopRaw;
2145
+ const marginBottom = marginBottomRaw !== null && marginBottomRaw !== void 0 ? marginBottomRaw : marginTopRaw; // "20px 0" → top=20px, bottom=20px
2146
+ // Outlook requires align on the outer TD to correctly position
2147
+ // a fixed-width inner table (e.g. width="300px").
2148
+ const alignAttr = align === "left" ? "left" : align === "right" ? "right" : "center";
2149
+ return (jsxs("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2150
+ position: "relative", // dev overlay anchor
2097
2151
  width: "100%",
2098
2152
  borderCollapse: "collapse",
2099
- }, 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: {
2153
+ border: "0",
2154
+ }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { align: alignAttr, style: {
2155
+ paddingTop: marginTop,
2156
+ paddingBottom: marginBottom,
2157
+ paddingLeft: "0",
2158
+ paddingRight: "0",
2159
+ fontSize: "0",
2160
+ lineHeight: "0",
2161
+ }, children: jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: alignAttr, style: {
2162
+ width: width,
2163
+ borderCollapse: "collapse",
2164
+ border: "0",
2165
+ }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { ...{ height: heightPx }, ref: (el) => {
2166
+ if (!el)
2167
+ return;
2168
+ el.setAttribute("style", `height:${height};` +
2169
+ `line-height:${height};` +
2170
+ `font-size:0;` +
2171
+ `padding:0;` +
2172
+ `background-color:${color};` +
2173
+ `mso-line-height-rule:exactly;`);
2174
+ }, style: {
2175
+ // Fallback for non-Outlook clients (React-rendered style object).
2100
2176
  height: height,
2177
+ lineHeight: height,
2101
2178
  fontSize: "0",
2102
- lineHeight: "0",
2103
2179
  padding: "0",
2104
- }, children: "\u00A0" }) }) }) })) }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
2180
+ backgroundColor: color,
2181
+ } }) }) }) }) }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
2105
2182
  }
2106
2183
  var Divider_default = memo(Divider, arePropsEqual);
2107
2184
 
@@ -2279,7 +2356,7 @@ function Head({ children, backgroundColor = "#ffffff", title = "Email Preview",
2279
2356
  }
2280
2357
  h1, h2, h3, h4, h5, h6 { margin: 0; padding: 0; font-weight: inherit; }
2281
2358
  `;
2282
- 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 } })] }));
2359
+ 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 } })] }));
2283
2360
  }
2284
2361
 
2285
2362
  function Heading({ config, devMode, children }) {
@@ -2346,6 +2423,27 @@ function Html({ children, backgroundColor = "#ffffff", }) {
2346
2423
  );
2347
2424
  }
2348
2425
 
2426
+ // Helper to build link href based on innerLink type
2427
+ function buildLinkHref$1(innerLink) {
2428
+ if (!innerLink || innerLink.type === "none")
2429
+ return null;
2430
+ switch (innerLink.type) {
2431
+ case "url":
2432
+ return innerLink.url || null;
2433
+ case "email":
2434
+ return innerLink.email ? `mailto:${innerLink.email}` : null;
2435
+ case "phone":
2436
+ return innerLink.phone ? `tel:${innerLink.phone}` : null;
2437
+ case "anchor":
2438
+ return innerLink.anchor ? `#${innerLink.anchor}` : null;
2439
+ case "page_top":
2440
+ return "#top";
2441
+ case "page_bottom":
2442
+ return "#bottom";
2443
+ default:
2444
+ return null;
2445
+ }
2446
+ }
2349
2447
  function getBorderStyle$3(border) {
2350
2448
  if (!border)
2351
2449
  return {};
@@ -2387,8 +2485,11 @@ function getBorderStyleString$1(border) {
2387
2485
  return styles.join(" ");
2388
2486
  }
2389
2487
  function Image({ config, devNode, devMode }) {
2390
- var _a;
2391
- const { src, alt, href, target, mobile } = config;
2488
+ var _a, _b;
2489
+ const { src, alt, innerLink, mobile } = config;
2490
+ // Resolve href and target from innerLink
2491
+ const href = buildLinkHref$1(innerLink);
2492
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
2392
2493
  const seed = src + (alt || "");
2393
2494
  const instanceId = seed
2394
2495
  .split("")
@@ -2403,38 +2504,89 @@ function Image({ config, devNode, devMode }) {
2403
2504
  // Determine the table's "initial" width.
2404
2505
  // If it's 300px, the table should be 300px, not 100%.
2405
2506
  const tableWidth = isPercent ? desktopWidth : `${widthAttr}px`;
2406
- // 2. Mobile Overrides (Every property used)
2507
+ // When width is a percentage, Outlook ignores CSS and renders the image at
2508
+ // its intrinsic pixel size. Setting a concrete `width` HTML attribute gives
2509
+ // Outlook a value to constrain against while modern clients continue to use
2510
+ // the CSS `width: 100%` for fluid rendering.
2511
+ //
2512
+ // If `maxWidth` is a pixel value (e.g. "600px"), we extract the number and
2513
+ // use it as the HTML `width` attribute so Outlook enforces that cap.
2514
+ // Other clients ignore the attribute and rely on CSS styles instead.
2515
+ // If `maxWidth` is not set or is not a pixel value (e.g. "100%"), we fall
2516
+ // back to the original behaviour (numeric string for px widths, undefined
2517
+ // for % widths).
2518
+ const maxWidthPx = ((_b = config.maxWidth) === null || _b === void 0 ? void 0 : _b.endsWith("px"))
2519
+ ? parseInt(config.maxWidth, 10)
2520
+ : undefined;
2521
+ const imgWidthAttr = isPercent ? (maxWidthPx !== null && maxWidthPx !== void 0 ? maxWidthPx : undefined) : widthAttr;
2522
+ // 2. Mobile Overrides — only emit CSS properties that are explicitly set,
2523
+ // so unspecified properties are left untouched (no forced defaults).
2407
2524
  let mobileCss = "";
2408
2525
  if (mobile) {
2526
+ // .wrap-${imgClass} rules
2527
+ const wrapRules = [
2528
+ // Always reset min-width so the px lock from desktop can be overridden
2529
+ "min-width: 0 !important;",
2530
+ ];
2531
+ if (mobile.width !== undefined)
2532
+ wrapRules.push(`width: ${mobile.width} !important;`);
2533
+ if (mobile.maxWidth !== undefined)
2534
+ wrapRules.push(`max-width: ${mobile.maxWidth} !important;`);
2535
+ // .td-${imgClass} rules
2536
+ const tdRules = [];
2537
+ if (mobile.padding !== undefined)
2538
+ tdRules.push(`padding: ${mobile.padding} !important;`);
2539
+ if (mobile.backgroundColor !== undefined)
2540
+ tdRules.push(`background-color: ${mobile.backgroundColor} !important;`);
2541
+ // .${imgClass} rules
2542
+ const imgRules = [];
2543
+ if (mobile.width !== undefined)
2544
+ imgRules.push(`width: ${mobile.width} !important;`);
2545
+ if (mobile.height !== undefined)
2546
+ imgRules.push(`height: ${mobile.height} !important;`);
2547
+ if (mobile.maxWidth !== undefined)
2548
+ imgRules.push(`max-width: ${mobile.maxWidth} !important;`);
2549
+ if (mobile.maxHeight !== undefined)
2550
+ imgRules.push(`max-height: ${mobile.maxHeight} !important;`);
2551
+ if (mobile.borderRadius !== undefined)
2552
+ imgRules.push(`border-radius: ${mobile.borderRadius} !important;`);
2553
+ if (mobile.hidden !== undefined)
2554
+ imgRules.push(`display: ${mobile.hidden ? "none" : "block"} !important;`);
2555
+ if (mobile.objectFit !== undefined)
2556
+ imgRules.push(`object-fit: ${mobile.objectFit} !important;`);
2557
+ if (mobile.objectPosition !== undefined)
2558
+ imgRules.push(`object-position: ${mobile.objectPosition} !important;`);
2559
+ if (mobile.border !== undefined)
2560
+ imgRules.push(getBorderStyleString$1(mobile.border));
2409
2561
  mobileCss = `
2410
2562
  @media screen and (max-width: 768px) {
2411
2563
  .wrap-${imgClass} {
2412
2564
  /* This breaks the px lock from desktop and makes it fluid */
2413
- width: ${mobile.width || "100%"} !important;
2414
- max-width: ${mobile.maxWidth || "100%"} !important;
2415
- min-width: 0 !important;
2565
+ ${wrapRules.join("\n ")}
2416
2566
  }
2417
2567
  .td-${imgClass} {
2418
- padding: ${mobile.padding || "0"} !important;
2419
- background-color: ${mobile.backgroundColor || "transparent"} !important;
2420
- width: 100% !important;
2568
+ ${tdRules.join("\n ")}
2421
2569
  }
2422
2570
  .${imgClass} {
2423
- width: ${mobile.width || "100%"} !important;
2424
- height: ${mobile.height || "auto"} !important;
2425
- max-width: ${mobile.maxWidth || "100%"} !important;
2426
- max-height: ${mobile.maxHeight || "none"} !important;
2427
- border-radius: ${mobile.borderRadius || "0"} !important;
2428
- display: ${mobile.hidden ? "none" : "block"} !important;
2429
- object-fit: ${mobile.objectFit || "fill"} !important;
2430
- object-position: ${mobile.objectPosition || "center"} !important;
2431
- ${getBorderStyleString$1(mobile.border)}
2571
+ ${imgRules.join("\n ")}
2432
2572
  }
2433
2573
  }
2434
2574
  `;
2435
2575
  }
2436
- const imgStyle = Object.assign(Object.assign({ display: "block", width: isPercent ? "100%" : desktopWidth, height: config.height || "auto", maxWidth: config.maxWidth || "100%", maxHeight: config.maxHeight || "none", borderRadius: config.borderRadius || "0" }, getBorderStyle$3(config.border)), { outline: "none", textDecoration: "none", objectFit: config.objectFit, objectPosition: config.objectPosition });
2437
- const imageElement = (jsx("img", { src: src, alt: alt, width: !isPercent ? widthAttr : undefined, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle }));
2576
+ const imgStyle = {
2577
+ display: "block",
2578
+ width: isPercent ? "100%" : desktopWidth,
2579
+ height: config.height || "auto",
2580
+ maxWidth: config.maxWidth || "100%",
2581
+ maxHeight: config.maxHeight || "none",
2582
+ borderRadius: config.borderRadius || "0",
2583
+ ...getBorderStyle$3(config.border),
2584
+ outline: "none",
2585
+ textDecoration: "none",
2586
+ objectFit: config.objectFit,
2587
+ objectPosition: config.objectPosition,
2588
+ };
2589
+ const imageElement = (jsx("img", { src: src, alt: alt, width: imgWidthAttr, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle, draggable: !devMode }));
2438
2590
  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
2439
2591
  , style: {
2440
2592
  width: tableWidth, // Fixed px here prevents the 100% "ghost space"
@@ -2447,7 +2599,9 @@ function Image({ config, devNode, devMode }) {
2447
2599
  fontSize: "0",
2448
2600
  lineHeight: "0",
2449
2601
  width: tableWidth, // Lock the cell as well
2450
- }, 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 }) }) }))] })] }));
2602
+ }, children: href && !devMode ? (jsx("a", { href: href, target: target, ...(target === "_blank"
2603
+ ? { rel: "noopener noreferrer" }
2604
+ : {}), style: { display: "block", width: "100%" }, children: imageElement })) : (imageElement) }) }) }), devMode && !!devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] })] }));
2451
2605
  }
2452
2606
  var Image_default = memo(Image, arePropsEqual);
2453
2607
 
@@ -2521,13 +2675,30 @@ function Row({ children, config, devNode, devMode }) {
2521
2675
  const childrenArray = (Array.isArray(children) ? children : [children]).filter((child) => child != null);
2522
2676
  const numChildren = childrenArray.length;
2523
2677
  const href = getHrefFromInnerLink(config.innerLink);
2524
- const target = (_a = config.innerLink) === null || _a === void 0 ? void 0 : _a.target;
2678
+ const target = ((_a = config.innerLink) === null || _a === void 0 ? void 0 : _a.target) || "_blank";
2525
2679
  // 1. Outer TD: Background, Border Radius, Width, Height.
2526
- const backgroundTdStyle = Object.assign({ backgroundColor: config.backgroundColor, borderRadius: config.borderRadius, width: config.width || "100%", height: config.height, backgroundImage: config.backgroundImage
2680
+ const backgroundTdStyle = {
2681
+ backgroundColor: config.backgroundColor,
2682
+ borderRadius: config.borderRadius,
2683
+ width: config.width || "100%",
2684
+ height: config.height,
2685
+ backgroundImage: config.backgroundImage
2527
2686
  ? `url(${config.backgroundImage.src})`
2528
- : 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" }));
2687
+ : undefined,
2688
+ backgroundRepeat: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.repeat,
2689
+ backgroundSize: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.size,
2690
+ backgroundPosition: (_d = config.backgroundImage) === null || _d === void 0 ? void 0 : _d.position,
2691
+ ...(config.borderRadius && { overflow: "hidden" }),
2692
+ };
2529
2693
  // 2. Inner Table: Border and Border Radius.
2530
- const borderTableStyle = Object.assign({ width: "100%", height: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: config.borderRadius }, getBorderStyle$2(config.border));
2694
+ const borderTableStyle = {
2695
+ width: "100%",
2696
+ height: "100%",
2697
+ borderCollapse: "separate",
2698
+ borderSpacing: 0,
2699
+ borderRadius: config.borderRadius,
2700
+ ...getBorderStyle$2(config.border),
2701
+ };
2531
2702
  // 3. Padding TD.
2532
2703
  const paddingTdStyle = {
2533
2704
  padding: config.padding,
@@ -2545,7 +2716,13 @@ function Row({ children, config, devNode, devMode }) {
2545
2716
  // Content table fills available space, giving Outlook Classic a hard
2546
2717
  // boundary so text children get a constrained box and line wrapping
2547
2718
  // triggers correctly. Use for rows containing text + image layouts.
2548
- const contentTableStyle = Object.assign({ width: config.fillWidth ? "100%" : "auto", height: "100%", borderCollapse: "collapse", minWidth: "1px" }, (!config.fillWidth && { maxWidth: config.width || "100%" }));
2719
+ const contentTableStyle = {
2720
+ width: config.fillWidth ? "100%" : "auto",
2721
+ height: "100%",
2722
+ borderCollapse: "collapse",
2723
+ minWidth: "1px",
2724
+ ...(!config.fillWidth && { maxWidth: config.width || "100%" }),
2725
+ };
2549
2726
  // 5. Gap TD.
2550
2727
  const gapTdStyle = {
2551
2728
  width: config.gap || "0",
@@ -2556,28 +2733,28 @@ function Row({ children, config, devNode, devMode }) {
2556
2733
  ? justifyMap$1[config.justifyContent]
2557
2734
  : "left";
2558
2735
  const tdValign = config.alignItems ? alignMap[config.alignItems] : "top";
2559
- const content = (jsxs("table", Object.assign({ "aria-label": "Row Outer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2736
+ const content = (jsxs("table", { "aria-label": "Row Outer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2560
2737
  position: "relative",
2561
2738
  width: config.width || "100%",
2562
2739
  height: config.height,
2563
2740
  borderCollapse: "collapse",
2564
- } }, (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: {
2741
+ }, ...(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: {
2565
2742
  width: "100%",
2566
2743
  height: "100%",
2567
2744
  borderCollapse: "collapse",
2568
- }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { align: tdAlign, width: "100%", style: { width: "100%" }, 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: {
2745
+ }, 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: {
2569
2746
  verticalAlign: tdValign,
2570
2747
  textAlign: "left",
2571
2748
  padding: "0",
2572
2749
  margin: "0",
2573
2750
  }, className: "child-cell", children: child }), index < numChildren - 1 &&
2574
- 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 }) }) }))] })));
2751
+ 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 }) }) }))] }));
2575
2752
  if (href && !devMode) {
2576
- return (jsx("a", Object.assign({ href: href }, (target && { target }), { style: {
2753
+ return (jsx("a", { href: href, ...({ target }), style: {
2577
2754
  textDecoration: "none",
2578
2755
  color: "inherit",
2579
2756
  display: "block",
2580
- }, children: content })));
2757
+ }, children: content }));
2581
2758
  }
2582
2759
  return content;
2583
2760
  }
@@ -2621,9 +2798,18 @@ function getBorderStyle$1(border) {
2621
2798
  const Section = ({ config, children, devNode, }) => {
2622
2799
  var _a, _b, _c;
2623
2800
  const { sectionType, padding } = config;
2624
- 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
2801
+ return (jsxs("table", { "aria-label": `Section |Table | ${sectionType}`, role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2802
+ position: "relative",
2803
+ width: "100%",
2804
+ backgroundColor: config.backgroundColor,
2805
+ ...getBorderStyle$1(config.border),
2806
+ backgroundImage: config.backgroundImage
2625
2807
  ? `url(${config.backgroundImage.src})`
2626
- : 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: {
2808
+ : undefined,
2809
+ backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat,
2810
+ backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size,
2811
+ backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position,
2812
+ }, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: {
2627
2813
  padding: padding,
2628
2814
  }, children: children }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsxs("td", { children: [jsxs("span", { style: {
2629
2815
  backgroundColor: "black",
@@ -2664,11 +2850,14 @@ function Spacer({ config, devNode }) {
2664
2850
  const spacerHeightAttribute = parseInt(height, 10) || 1;
2665
2851
  return (
2666
2852
  // Outer table ensures the spacer spans the full width of its container
2667
- jsxs("table", Object.assign({ "aria-label": "Vertical Spacer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: Object.assign({
2853
+ jsxs("table", { "aria-label": "Vertical Spacer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2668
2854
  // --- Start dev
2669
- position: "relative" }, spacerTableStyle) }, { height: spacerHeightAttribute }, { className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: spacerTdStyle,
2855
+ position: "relative",
2856
+ // --- End dev
2857
+ ...spacerTableStyle,
2858
+ }, ...{ height: spacerHeightAttribute }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: spacerTdStyle,
2670
2859
  // Explicit height attribute
2671
- height: spacerHeightAttribute, children: "\u00A0" }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] })));
2860
+ height: spacerHeightAttribute, children: "\u00A0" }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
2672
2861
  }
2673
2862
  var Spacer_default = memo(Spacer, arePropsEqual);
2674
2863
 
@@ -2807,7 +2996,7 @@ function buildIconifyUrl(config) {
2807
2996
  const template = process.env.ICONIFY_API_IMAGE_URI ||
2808
2997
  "https://iconify.pagenflow.com/api/image/{{height}}/{{color}}/{{rotate}}-{{rotate-orientation}}/{{icon-full-name}}.png";
2809
2998
  return template
2810
- .replace("{{height}}", String(numericHeight * 2))
2999
+ .replace("{{height}}", String(numericHeight * 4))
2811
3000
  .replace("{{color}}", cleanColor)
2812
3001
  .replace("{{rotate}}", String(rotate))
2813
3002
  .replace("{{rotate-orientation}}", rotateOrientation)
@@ -2841,7 +3030,7 @@ function Icon({ config, devNode, devMode, children }) {
2841
3030
  // Determine icon source
2842
3031
  const iconSrc = buildIconifyUrl(config);
2843
3032
  const href = buildLinkHref(innerLink);
2844
- const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_self";
3033
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
2845
3034
  const align = justifyMap[justifyContent];
2846
3035
  // Get border styles
2847
3036
  const borderStyle = getBorderStyle(border);
@@ -2884,7 +3073,13 @@ function Icon({ config, devNode, devMode, children }) {
2884
3073
  lineHeight: "0",
2885
3074
  };
2886
3075
  // 4. Inner Table Style: Apply border here with border-collapse: separate
2887
- const innerTableStyle = Object.assign({ width: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: borderRadius }, borderStyle);
3076
+ const innerTableStyle = {
3077
+ width: "100%",
3078
+ borderCollapse: "separate",
3079
+ borderSpacing: 0,
3080
+ borderRadius: borderRadius,
3081
+ ...borderStyle,
3082
+ };
2888
3083
  // 5. Inner TD Style: Padding
2889
3084
  const innerTdStyle = {
2890
3085
  padding: padding,
@@ -2932,7 +3127,7 @@ function Icon({ config, devNode, devMode, children }) {
2932
3127
  // Icon image element
2933
3128
  const iconElement = devMode && !!children ? (children) : iconSrc ? (jsx("img", { draggable: false, src: iconSrc, alt: "", style: imgStyle, width: widthNum, height: heightNum, border: 0 })) : (jsx(Fragment$1, {}));
2934
3129
  // Wrap in link if href exists and not in dev mode
2935
- const content = href && !devMode ? (jsx("a", Object.assign({ href: href, target: target, style: linkStyle }, (target === "_blank" ? { rel: "noopener noreferrer" } : {}), { children: iconElement }))) : (iconElement);
3130
+ const content = href && !devMode ? (jsx("a", { href: href, target: target, style: linkStyle, ...(target === "_blank" ? { rel: "noopener noreferrer" } : {}), children: iconElement })) : (iconElement);
2936
3131
  // Build the HTML content with VML support (only when NOT in dev mode)
2937
3132
  const useVML = !devMode && backgroundColor && numericBorderRadius > 0;
2938
3133
  const htmlContent = useVML