@pagenflow/email 1.4.3 → 1.4.5

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
@@ -1537,6 +1537,27 @@ const justifyMap$3 = {
1537
1537
  center: "center",
1538
1538
  end: "right",
1539
1539
  };
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;
1559
+ }
1560
+ }
1540
1561
  function getBorderStyleString$2(border) {
1541
1562
  if (!border)
1542
1563
  return "";
@@ -1572,8 +1593,10 @@ function getBorderStyleString$2(border) {
1572
1593
  return styles.join(" ");
1573
1594
  }
1574
1595
  function Button({ config, devMode }) {
1575
- const { href, children, backgroundColor = "#007bff", // Default blue
1576
- 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";
1577
1600
  // Sanitize fontFamily early so safeFontFamily is available for all paths below.
1578
1601
  const safeFontFamily = fontFamily
1579
1602
  ? fontFamily.replace(/['"]/g, "")
@@ -1593,23 +1616,27 @@ function Button({ config, devMode }) {
1593
1616
  // Check if width is percentage-based or not defined
1594
1617
  const isPercentageWidth = !width || width.includes("%");
1595
1618
  const useSimpleOutlookApproach = isPercentageWidth;
1596
- const align = justifyMap$3[justifyContent];
1619
+ const align = justifyContent ? justifyMap$3[justifyContent] : undefined;
1597
1620
  // --- VML Calculation and Code for Outlook Compatibility (Fixed Width Only) ---
1598
1621
  let vmlButton = "";
1599
1622
  if (!useSimpleOutlookApproach) {
1600
1623
  // VML needs fixed pixel height. We estimate it based on padding and potential wrapping.
1601
- const numericPadding = parseInt(padding.split(" ")[0] || "12", 10);
1602
- const numericFontSize = parseInt(fontSize, 10);
1603
- const numericLineHeight = lineHeight.includes("px")
1604
- ? parseInt(lineHeight, 10)
1605
- : numericFontSize * parseFloat(lineHeight);
1624
+ const numericPadding = padding
1625
+ ? parseInt(padding.split(" ")[0] || "12", 10)
1626
+ : 12;
1627
+ const numericFontSize = fontSize ? parseInt(fontSize, 10) : 0;
1628
+ const numericLineHeight = lineHeight
1629
+ ? lineHeight.includes("px")
1630
+ ? parseInt(lineHeight, 10)
1631
+ : numericFontSize * parseFloat(lineHeight)
1632
+ : numericFontSize;
1606
1633
  // Trust user's explicit pixel width - no calculation needed
1607
1634
  const vmlWidth = parseInt(width, 10);
1608
1635
  // Calculate VML height - trust user's padding and let text wrap naturally
1609
1636
  // VML v:textbox will handle text wrapping automatically
1610
1637
  const textContent = typeof children === "string" ? children : "";
1611
1638
  // Estimate number of lines based on text length and button width
1612
- const horizontalPadding = padding.split(" ")[1]
1639
+ const horizontalPadding = (padding === null || padding === void 0 ? void 0 : padding.split(" ")[1])
1613
1640
  ? parseInt(padding.split(" ")[1], 10) * 2
1614
1641
  : numericPadding * 2;
1615
1642
  const availableTextWidth = vmlWidth - horizontalPadding;
@@ -1622,15 +1649,17 @@ function Button({ config, devMode }) {
1622
1649
  // Add extra 4px buffer to prevent bottom cropping in VML
1623
1650
  const vmlHeight = Math.max(numericPadding * 2 + textHeight + 4, 40);
1624
1651
  // VML colors must use the full hex format (e.g., #000000)
1625
- const vmlFillColor = backgroundColor.startsWith("#")
1626
- ? backgroundColor
1627
- : `#${backgroundColor}`;
1652
+ const vmlFillColor = backgroundColor
1653
+ ? backgroundColor.startsWith("#")
1654
+ ? backgroundColor
1655
+ : `#${backgroundColor}`
1656
+ : undefined;
1628
1657
  // VML stroke color for border
1629
1658
  const vmlStrokeColor = (border === null || border === void 0 ? void 0 : border.color) || vmlFillColor;
1630
1659
  const vmlStrokeWeight = (border === null || border === void 0 ? void 0 : border.width) ? parseInt(border.width, 10) : 0;
1631
1660
  const hasVmlStroke = vmlStrokeWeight > 0;
1632
1661
  // Build VML font styles - consistent with other rendering paths
1633
- const vmlFontWeight = fontWeight || "500";
1662
+ const vmlFontWeight = fontWeight;
1634
1663
  const vmlFontStyle = fontStyle === "italic" ? "font-style:italic;" : "";
1635
1664
  const vmlLetterSpacing = letterSpacing
1636
1665
  ? `letter-spacing:${letterSpacing};`
@@ -1641,12 +1670,12 @@ function Button({ config, devMode }) {
1641
1670
  const vmlTextDecoration = textDecoration && textDecoration !== "none"
1642
1671
  ? `text-decoration:${textDecoration};`
1643
1672
  : "";
1644
- const vmlWhiteSpace = whiteSpace !== "normal" ? `white-space:${whiteSpace};` : "";
1673
+ const vmlWhiteSpace = whiteSpace ? `white-space:${whiteSpace};` : "";
1645
1674
  const vmlDirection = direction ? `direction:${direction};` : "";
1646
1675
  const vmlOpacity = opacity !== undefined ? `opacity:${opacity};` : "";
1647
1676
  // VML code uses MSO conditional comments to render only in Outlook
1648
1677
  // Use table with explicit MSO height for vertical centering
1649
- const horizontalPaddingValue = padding.split(" ")[1]
1678
+ const horizontalPaddingValue = (padding === null || padding === void 0 ? void 0 : padding.split(" ")[1])
1650
1679
  ? parseInt(padding.split(" ")[1], 10)
1651
1680
  : numericPadding;
1652
1681
  // For VML, we need to use a table inside to properly apply padding and centering
@@ -1655,7 +1684,7 @@ function Button({ config, devMode }) {
1655
1684
  if (textAlign === "center") {
1656
1685
  vmlAlignAttr = 'align="center"';
1657
1686
  }
1658
- else {
1687
+ else if (textAlign) {
1659
1688
  vmlAlignStyle = `text-align:${textAlign};`;
1660
1689
  }
1661
1690
  // Border radius is intentionally omitted (arcsize="0%") for Outlook Classic.
@@ -1663,12 +1692,12 @@ function Button({ config, devMode }) {
1663
1692
  // is inconsistent, so we render sharp corners there instead.
1664
1693
  vmlButton = `
1665
1694
  <!--[if mso]>
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}">
1695
+ <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}"` : ""}>
1667
1696
  <w:anchorlock/>
1668
1697
  <v:textbox inset="${horizontalPaddingValue}px,${numericPadding}px,${horizontalPaddingValue}px,${numericPadding}px">
1669
1698
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse:collapse;">
1670
1699
  <tr>
1671
- <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;">
1700
+ <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;">
1672
1701
  ${typeof children === "string" ? children : ""}
1673
1702
  </td>
1674
1703
  </tr>
@@ -1692,10 +1721,10 @@ function Button({ config, devMode }) {
1692
1721
  const textTransformProp = textTransform
1693
1722
  ? `text-transform: ${textTransform};`
1694
1723
  : "";
1695
- const whiteSpaceProp = whiteSpace !== "normal" ? `white-space: ${whiteSpace};` : "";
1724
+ const whiteSpaceProp = whiteSpace ? `white-space: ${whiteSpace};` : "";
1696
1725
  const directionProp = direction ? `direction: ${direction};` : "";
1697
1726
  const opacityProp = opacity !== undefined ? `opacity: ${opacity};` : "";
1698
- const wordBreakProp = wordBreak !== "break-word" ? `word-break: ${wordBreak};` : "";
1727
+ const wordBreakProp = wordBreak ? `word-break: ${wordBreak};` : "";
1699
1728
  // Border radius is intentionally omitted from the Outlook Classic table cell.
1700
1729
  // Outlook Classic ignores border-radius on table cells anyway, and including it
1701
1730
  // can cause unexpected rendering artifacts, so we explicitly leave it out.
@@ -1703,13 +1732,17 @@ function Button({ config, devMode }) {
1703
1732
  <!--[if mso]>
1704
1733
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse;">
1705
1734
  <tr>
1706
- <td align="${align}" style="padding: 0;">
1735
+ <td ${align ? `align="${align}"` : ""} style="padding: 0;">
1707
1736
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="${width || "auto"}" style="border-collapse: collapse;">
1708
1737
  <tr>
1709
- <td bgcolor="${backgroundColor}" align="${textAlign}" style="padding: ${padding}; text-align: ${textAlign}; ${borderStyleString}">
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;">
1711
- ${typeof children === "string" ? children : ""}
1712
- </a>
1738
+ <td ${backgroundColor ? `bgcolor="${backgroundColor}"` : ""} ${textAlign ? `align="${textAlign}"` : ""} style="${padding ? `padding: ${padding};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${borderStyleString}">
1739
+ ${href
1740
+ ? `<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;">
1741
+ ${typeof children === "string" ? children : ""}
1742
+ </a>`
1743
+ : `<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;">
1744
+ ${typeof children === "string" ? children : ""}
1745
+ </span>`}
1713
1746
  </td>
1714
1747
  </tr>
1715
1748
  </table>
@@ -1723,7 +1756,7 @@ function Button({ config, devMode }) {
1723
1756
  // fontFamily uses the sanitized value so embedded quotes never break the
1724
1757
  // style attribute string (which is always wrapped in double quotes).
1725
1758
  const sharedTextStyles = [
1726
- `color: ${color};`,
1759
+ color ? `color: ${color};` : "",
1727
1760
  safeFontFamily ? `font-family: ${safeFontFamily};` : "",
1728
1761
  fontSize ? `font-size: ${fontSize};` : "",
1729
1762
  fontWeight ? `font-weight: ${fontWeight};` : "",
@@ -1736,7 +1769,7 @@ function Button({ config, devMode }) {
1736
1769
  : "",
1737
1770
  direction ? `direction: ${direction};` : "",
1738
1771
  opacity !== undefined ? `opacity: ${opacity};` : "",
1739
- whiteSpace !== "normal" ? `white-space: ${whiteSpace};` : "",
1772
+ whiteSpace ? `white-space: ${whiteSpace};` : "",
1740
1773
  ]
1741
1774
  .filter(Boolean)
1742
1775
  .join(" ");
@@ -1770,20 +1803,24 @@ function Button({ config, devMode }) {
1770
1803
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse; width: 100%;">
1771
1804
  <tbody>
1772
1805
  <tr>
1773
- <td style="background-color: ${backgroundTdStyle.backgroundColor}; border-radius: ${backgroundTdStyle.borderRadius}; width: ${backgroundTdStyle.width}; ${maxWidth ? `max-width: ${maxWidth};` : ""} ${borderRadius ? "overflow: hidden;" : ""}">
1774
- <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: separate; border-spacing: 0; border-radius: ${borderRadius}; width: 100%; ${borderStyleString}">
1806
+ <td style="${backgroundTdStyle.backgroundColor ? `background-color: ${backgroundTdStyle.backgroundColor};` : ""} ${backgroundTdStyle.borderRadius ? `border-radius: ${backgroundTdStyle.borderRadius};` : ""} width: ${backgroundTdStyle.width}; ${maxWidth ? `max-width: ${maxWidth};` : ""} ${borderRadius ? "overflow: hidden;" : ""}">
1807
+ <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: separate; border-spacing: 0; ${borderRadius ? `border-radius: ${borderRadius};` : ""} width: 100%; ${borderStyleString}">
1775
1808
  <tbody>
1776
1809
  <tr>
1777
1810
  <td style="padding: 0;">
1778
1811
  ${devMode
1779
- ? `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; word-break: ${wordBreak}; text-align: ${textAlign}; padding: ${padding};">
1812
+ ? `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; ${wordBreak ? `word-break: ${wordBreak};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${padding ? `padding: ${padding};` : ""}">
1780
1813
  ${typeof children === "string" ? children : ""}
1781
1814
  </span>`
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};">
1783
- <span>
1815
+ : href
1816
+ ? `<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};` : ""}">
1817
+ <span>
1818
+ ${typeof children === "string" ? children : ""}
1819
+ </span>
1820
+ </a>`
1821
+ : `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; ${wordBreak ? `word-break: ${wordBreak};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${padding ? `padding: ${padding};` : ""}">
1784
1822
  ${typeof children === "string" ? children : ""}
1785
- </span>
1786
- </a>`}
1823
+ </span>`}
1787
1824
  </td>
1788
1825
  </tr>
1789
1826
  </tbody>
@@ -2102,40 +2139,48 @@ var Container_default = memo(Container, arePropsEqual);
2102
2139
 
2103
2140
  function Divider({ config, devNode }) {
2104
2141
  const { height = "1px", color = "#cccccc", width = "100%", margin = "20px 0", align = "center", hideOnMobile, } = config;
2105
- // 1. Outer TD Style: Applies the vertical spacing (margin)
2106
- const outerTdStyle = {
2107
- padding: margin,
2108
- fontSize: "0",
2109
- lineHeight: "0",
2110
- width: "100%",
2111
- };
2112
- // 2. Divider Table Style: Applies the line properties
2113
- const dividerTableStyle = {
2114
- width: width,
2115
- height: height,
2116
- backgroundColor: color,
2117
- borderCollapse: "collapse",
2118
- border: "0",
2119
- // ✅ FIX 1: Use string literal indexing for MSO properties
2120
- // ["mso-table-lspace" as string]: "0pt",
2121
- ["msoTableLspace"]: "0pt",
2122
- // ["mso-table-rspace" as string]: "0pt",
2123
- ["msoTableRspace"]: "0pt",
2124
- };
2125
- // Parse height for the HTML attribute
2126
- const dividerHeightAttribute = parseInt(height, 10) || 1;
2127
- return (jsxs("table", { "aria-label": "Divider Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2128
- // --- Start dev
2129
- position: "relative",
2130
- // --- End dev
2142
+ const heightPx = parseInt(height, 10) || 1;
2143
+ // Parse margin into paddingTop / paddingBottom for the outer TD.
2144
+ // Outlook ignores shorthand "20px 0" on TDs — must be explicit.
2145
+ const [marginTopRaw = "0", marginRightRaw = "0", marginBottomRaw, marginLeftRaw] = margin.trim().split(/\s+/);
2146
+ const marginTop = marginTopRaw;
2147
+ const marginBottom = marginBottomRaw !== null && marginBottomRaw !== void 0 ? marginBottomRaw : marginTopRaw; // "20px 0" → top=20px, bottom=20px
2148
+ // Outlook requires align on the outer TD to correctly position
2149
+ // a fixed-width inner table (e.g. width="300px").
2150
+ const alignAttr = align === "left" ? "left" : align === "right" ? "right" : "center";
2151
+ return (jsxs("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2152
+ position: "relative", // dev overlay anchor
2131
2153
  width: "100%",
2132
2154
  borderCollapse: "collapse",
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: {
2155
+ border: "0",
2156
+ }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { align: alignAttr, style: {
2157
+ paddingTop: marginTop,
2158
+ paddingBottom: marginBottom,
2159
+ paddingLeft: "0",
2160
+ paddingRight: "0",
2161
+ fontSize: "0",
2162
+ lineHeight: "0",
2163
+ }, children: jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: alignAttr, style: {
2164
+ width: width,
2165
+ borderCollapse: "collapse",
2166
+ border: "0",
2167
+ }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { ...{ height: heightPx }, ref: (el) => {
2168
+ if (!el)
2169
+ return;
2170
+ el.setAttribute("style", `height:${height};` +
2171
+ `line-height:${height};` +
2172
+ `font-size:0;` +
2173
+ `padding:0;` +
2174
+ `background-color:${color};` +
2175
+ `mso-line-height-rule:exactly;`);
2176
+ }, style: {
2177
+ // Fallback for non-Outlook clients (React-rendered style object).
2134
2178
  height: height,
2179
+ lineHeight: height,
2135
2180
  fontSize: "0",
2136
- lineHeight: "0",
2137
2181
  padding: "0",
2138
- }, children: "\u00A0" }) }) }) }) }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
2182
+ backgroundColor: color,
2183
+ } }) }) }) }) }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
2139
2184
  }
2140
2185
  var Divider_default = memo(Divider, arePropsEqual);
2141
2186
 
@@ -2380,6 +2425,27 @@ function Html({ children, backgroundColor = "#ffffff", }) {
2380
2425
  );
2381
2426
  }
2382
2427
 
2428
+ // Helper to build link href based on innerLink type
2429
+ function buildLinkHref$1(innerLink) {
2430
+ if (!innerLink || innerLink.type === "none")
2431
+ return null;
2432
+ switch (innerLink.type) {
2433
+ case "url":
2434
+ return innerLink.url || null;
2435
+ case "email":
2436
+ return innerLink.email ? `mailto:${innerLink.email}` : null;
2437
+ case "phone":
2438
+ return innerLink.phone ? `tel:${innerLink.phone}` : null;
2439
+ case "anchor":
2440
+ return innerLink.anchor ? `#${innerLink.anchor}` : null;
2441
+ case "page_top":
2442
+ return "#top";
2443
+ case "page_bottom":
2444
+ return "#bottom";
2445
+ default:
2446
+ return null;
2447
+ }
2448
+ }
2383
2449
  function getBorderStyle$3(border) {
2384
2450
  if (!border)
2385
2451
  return {};
@@ -2422,7 +2488,10 @@ function getBorderStyleString$1(border) {
2422
2488
  }
2423
2489
  function Image({ config, devNode, devMode }) {
2424
2490
  var _a, _b;
2425
- const { src, alt, href, target, mobile } = config;
2491
+ const { src, alt, innerLink, mobile } = config;
2492
+ // Resolve href and target from innerLink
2493
+ const href = buildLinkHref$1(innerLink);
2494
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
2426
2495
  const seed = src + (alt || "");
2427
2496
  const instanceId = seed
2428
2497
  .split("")
@@ -2452,32 +2521,56 @@ function Image({ config, devNode, devMode }) {
2452
2521
  ? parseInt(config.maxWidth, 10)
2453
2522
  : undefined;
2454
2523
  const imgWidthAttr = isPercent ? (maxWidthPx !== null && maxWidthPx !== void 0 ? maxWidthPx : undefined) : widthAttr;
2455
- // 2. Mobile Overrides (Every property used)
2524
+ // 2. Mobile Overrides only emit CSS properties that are explicitly set,
2525
+ // so unspecified properties are left untouched (no forced defaults).
2456
2526
  let mobileCss = "";
2457
2527
  if (mobile) {
2528
+ // .wrap-${imgClass} rules
2529
+ const wrapRules = [
2530
+ // Always reset min-width so the px lock from desktop can be overridden
2531
+ "min-width: 0 !important;",
2532
+ ];
2533
+ if (mobile.width !== undefined)
2534
+ wrapRules.push(`width: ${mobile.width} !important;`);
2535
+ if (mobile.maxWidth !== undefined)
2536
+ wrapRules.push(`max-width: ${mobile.maxWidth} !important;`);
2537
+ // .td-${imgClass} rules
2538
+ const tdRules = [];
2539
+ if (mobile.padding !== undefined)
2540
+ tdRules.push(`padding: ${mobile.padding} !important;`);
2541
+ if (mobile.backgroundColor !== undefined)
2542
+ tdRules.push(`background-color: ${mobile.backgroundColor} !important;`);
2543
+ // .${imgClass} rules
2544
+ const imgRules = [];
2545
+ if (mobile.width !== undefined)
2546
+ imgRules.push(`width: ${mobile.width} !important;`);
2547
+ if (mobile.height !== undefined)
2548
+ imgRules.push(`height: ${mobile.height} !important;`);
2549
+ if (mobile.maxWidth !== undefined)
2550
+ imgRules.push(`max-width: ${mobile.maxWidth} !important;`);
2551
+ if (mobile.maxHeight !== undefined)
2552
+ imgRules.push(`max-height: ${mobile.maxHeight} !important;`);
2553
+ if (mobile.borderRadius !== undefined)
2554
+ imgRules.push(`border-radius: ${mobile.borderRadius} !important;`);
2555
+ if (mobile.hidden !== undefined)
2556
+ imgRules.push(`display: ${mobile.hidden ? "none" : "block"} !important;`);
2557
+ if (mobile.objectFit !== undefined)
2558
+ imgRules.push(`object-fit: ${mobile.objectFit} !important;`);
2559
+ if (mobile.objectPosition !== undefined)
2560
+ imgRules.push(`object-position: ${mobile.objectPosition} !important;`);
2561
+ if (mobile.border !== undefined)
2562
+ imgRules.push(getBorderStyleString$1(mobile.border));
2458
2563
  mobileCss = `
2459
2564
  @media screen and (max-width: 768px) {
2460
2565
  .wrap-${imgClass} {
2461
2566
  /* 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;
2567
+ ${wrapRules.join("\n ")}
2465
2568
  }
2466
2569
  .td-${imgClass} {
2467
- padding: ${mobile.padding || "0"} !important;
2468
- background-color: ${mobile.backgroundColor || "transparent"} !important;
2469
- width: 100% !important;
2570
+ ${tdRules.join("\n ")}
2470
2571
  }
2471
2572
  .${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)}
2573
+ ${imgRules.join("\n ")}
2481
2574
  }
2482
2575
  }
2483
2576
  `;
@@ -2495,7 +2588,7 @@ function Image({ config, devNode, devMode }) {
2495
2588
  objectFit: config.objectFit,
2496
2589
  objectPosition: config.objectPosition,
2497
2590
  };
2498
- const imageElement = (jsx("img", { src: src, alt: alt, width: imgWidthAttr, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle }));
2591
+ const imageElement = (jsx("img", { src: src, alt: alt, width: imgWidthAttr, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle, draggable: !devMode }));
2499
2592
  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
2593
  , style: {
2501
2594
  width: tableWidth, // Fixed px here prevents the 100% "ghost space"
@@ -2508,7 +2601,9 @@ function Image({ config, devNode, devMode }) {
2508
2601
  fontSize: "0",
2509
2602
  lineHeight: "0",
2510
2603
  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 }) }) }))] })] }));
2604
+ }, children: href && !devMode ? (jsx("a", { href: href, target: target, ...(target === "_blank"
2605
+ ? { rel: "noopener noreferrer" }
2606
+ : {}), style: { display: "block", width: "100%" }, children: imageElement })) : (imageElement) }) }) }), devMode && !!devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] })] }));
2512
2607
  }
2513
2608
  var Image_default = memo(Image, arePropsEqual);
2514
2609
 
@@ -2582,7 +2677,7 @@ function Row({ children, config, devNode, devMode }) {
2582
2677
  const childrenArray = (Array.isArray(children) ? children : [children]).filter((child) => child != null);
2583
2678
  const numChildren = childrenArray.length;
2584
2679
  const href = getHrefFromInnerLink(config.innerLink);
2585
- const target = (_a = config.innerLink) === null || _a === void 0 ? void 0 : _a.target;
2680
+ const target = ((_a = config.innerLink) === null || _a === void 0 ? void 0 : _a.target) || "_blank";
2586
2681
  // 1. Outer TD: Background, Border Radius, Width, Height.
2587
2682
  const backgroundTdStyle = {
2588
2683
  backgroundColor: config.backgroundColor,
@@ -2657,7 +2752,7 @@ function Row({ children, config, devNode, devMode }) {
2657
2752
  }, className: "child-cell", children: child }), index < numChildren - 1 &&
2658
2753
  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 }) }) }))] }));
2659
2754
  if (href && !devMode) {
2660
- return (jsx("a", { href: href, ...(target && { target }), style: {
2755
+ return (jsx("a", { href: href, ...({ target }), style: {
2661
2756
  textDecoration: "none",
2662
2757
  color: "inherit",
2663
2758
  display: "block",
@@ -2769,7 +2864,7 @@ function Spacer({ config, devNode }) {
2769
2864
  var Spacer_default = memo(Spacer, arePropsEqual);
2770
2865
 
2771
2866
  function Text({ config, devMode, children }) {
2772
- const { text, padding, color, textAlign, fontFamily, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, backgroundColor, opacity, whiteSpace, wordBreak = "break-all", maxWidth } = config;
2867
+ const { text, padding, color, textAlign, fontFamily, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, backgroundColor, opacity, whiteSpace, wordBreak = "break-all", maxWidth, } = config;
2773
2868
  // 1. TD Style: Where padding and background are reliably applied.
2774
2869
  const tdStyle = {
2775
2870
  padding: padding,
@@ -2796,7 +2891,7 @@ function Text({ config, devMode, children }) {
2796
2891
  wordBreak: wordBreak,
2797
2892
  margin: "0",
2798
2893
  padding: "0",
2799
- maxWidth
2894
+ maxWidth,
2800
2895
  };
2801
2896
  // Determine content to render
2802
2897
  const content = text !== null && text !== void 0 ? text : children;
@@ -2903,7 +2998,7 @@ function buildIconifyUrl(config) {
2903
2998
  const template = process.env.ICONIFY_API_IMAGE_URI ||
2904
2999
  "https://iconify.pagenflow.com/api/image/{{height}}/{{color}}/{{rotate}}-{{rotate-orientation}}/{{icon-full-name}}.png";
2905
3000
  return template
2906
- .replace("{{height}}", String(numericHeight * 2))
3001
+ .replace("{{height}}", String(numericHeight * 4))
2907
3002
  .replace("{{color}}", cleanColor)
2908
3003
  .replace("{{rotate}}", String(rotate))
2909
3004
  .replace("{{rotate-orientation}}", rotateOrientation)
@@ -2937,7 +3032,7 @@ function Icon({ config, devNode, devMode, children }) {
2937
3032
  // Determine icon source
2938
3033
  const iconSrc = buildIconifyUrl(config);
2939
3034
  const href = buildLinkHref(innerLink);
2940
- const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_self";
3035
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
2941
3036
  const align = justifyMap[justifyContent];
2942
3037
  // Get border styles
2943
3038
  const borderStyle = getBorderStyle(border);