@pagenflow/email 1.4.3 → 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
@@ -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,25 @@ 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 ? 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;
1606
1631
  // Trust user's explicit pixel width - no calculation needed
1607
1632
  const vmlWidth = parseInt(width, 10);
1608
1633
  // Calculate VML height - trust user's padding and let text wrap naturally
1609
1634
  // VML v:textbox will handle text wrapping automatically
1610
1635
  const textContent = typeof children === "string" ? children : "";
1611
1636
  // Estimate number of lines based on text length and button width
1612
- const horizontalPadding = padding.split(" ")[1]
1637
+ const horizontalPadding = (padding === null || padding === void 0 ? void 0 : padding.split(" ")[1])
1613
1638
  ? parseInt(padding.split(" ")[1], 10) * 2
1614
1639
  : numericPadding * 2;
1615
1640
  const availableTextWidth = vmlWidth - horizontalPadding;
@@ -1622,15 +1647,17 @@ function Button({ config, devMode }) {
1622
1647
  // Add extra 4px buffer to prevent bottom cropping in VML
1623
1648
  const vmlHeight = Math.max(numericPadding * 2 + textHeight + 4, 40);
1624
1649
  // VML colors must use the full hex format (e.g., #000000)
1625
- const vmlFillColor = backgroundColor.startsWith("#")
1626
- ? backgroundColor
1627
- : `#${backgroundColor}`;
1650
+ const vmlFillColor = backgroundColor
1651
+ ? backgroundColor.startsWith("#")
1652
+ ? backgroundColor
1653
+ : `#${backgroundColor}`
1654
+ : undefined;
1628
1655
  // VML stroke color for border
1629
1656
  const vmlStrokeColor = (border === null || border === void 0 ? void 0 : border.color) || vmlFillColor;
1630
1657
  const vmlStrokeWeight = (border === null || border === void 0 ? void 0 : border.width) ? parseInt(border.width, 10) : 0;
1631
1658
  const hasVmlStroke = vmlStrokeWeight > 0;
1632
1659
  // Build VML font styles - consistent with other rendering paths
1633
- const vmlFontWeight = fontWeight || "500";
1660
+ const vmlFontWeight = fontWeight;
1634
1661
  const vmlFontStyle = fontStyle === "italic" ? "font-style:italic;" : "";
1635
1662
  const vmlLetterSpacing = letterSpacing
1636
1663
  ? `letter-spacing:${letterSpacing};`
@@ -1641,12 +1668,12 @@ function Button({ config, devMode }) {
1641
1668
  const vmlTextDecoration = textDecoration && textDecoration !== "none"
1642
1669
  ? `text-decoration:${textDecoration};`
1643
1670
  : "";
1644
- const vmlWhiteSpace = whiteSpace !== "normal" ? `white-space:${whiteSpace};` : "";
1671
+ const vmlWhiteSpace = whiteSpace ? `white-space:${whiteSpace};` : "";
1645
1672
  const vmlDirection = direction ? `direction:${direction};` : "";
1646
1673
  const vmlOpacity = opacity !== undefined ? `opacity:${opacity};` : "";
1647
1674
  // VML code uses MSO conditional comments to render only in Outlook
1648
1675
  // Use table with explicit MSO height for vertical centering
1649
- const horizontalPaddingValue = padding.split(" ")[1]
1676
+ const horizontalPaddingValue = (padding === null || padding === void 0 ? void 0 : padding.split(" ")[1])
1650
1677
  ? parseInt(padding.split(" ")[1], 10)
1651
1678
  : numericPadding;
1652
1679
  // For VML, we need to use a table inside to properly apply padding and centering
@@ -1655,7 +1682,7 @@ function Button({ config, devMode }) {
1655
1682
  if (textAlign === "center") {
1656
1683
  vmlAlignAttr = 'align="center"';
1657
1684
  }
1658
- else {
1685
+ else if (textAlign) {
1659
1686
  vmlAlignStyle = `text-align:${textAlign};`;
1660
1687
  }
1661
1688
  // Border radius is intentionally omitted (arcsize="0%") for Outlook Classic.
@@ -1663,12 +1690,12 @@ function Button({ config, devMode }) {
1663
1690
  // is inconsistent, so we render sharp corners there instead.
1664
1691
  vmlButton = `
1665
1692
  <!--[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}">
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}"` : ""}>
1667
1694
  <w:anchorlock/>
1668
1695
  <v:textbox inset="${horizontalPaddingValue}px,${numericPadding}px,${horizontalPaddingValue}px,${numericPadding}px">
1669
1696
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse:collapse;">
1670
1697
  <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;">
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;">
1672
1699
  ${typeof children === "string" ? children : ""}
1673
1700
  </td>
1674
1701
  </tr>
@@ -1692,10 +1719,10 @@ function Button({ config, devMode }) {
1692
1719
  const textTransformProp = textTransform
1693
1720
  ? `text-transform: ${textTransform};`
1694
1721
  : "";
1695
- const whiteSpaceProp = whiteSpace !== "normal" ? `white-space: ${whiteSpace};` : "";
1722
+ const whiteSpaceProp = whiteSpace ? `white-space: ${whiteSpace};` : "";
1696
1723
  const directionProp = direction ? `direction: ${direction};` : "";
1697
1724
  const opacityProp = opacity !== undefined ? `opacity: ${opacity};` : "";
1698
- const wordBreakProp = wordBreak !== "break-word" ? `word-break: ${wordBreak};` : "";
1725
+ const wordBreakProp = wordBreak ? `word-break: ${wordBreak};` : "";
1699
1726
  // Border radius is intentionally omitted from the Outlook Classic table cell.
1700
1727
  // Outlook Classic ignores border-radius on table cells anyway, and including it
1701
1728
  // can cause unexpected rendering artifacts, so we explicitly leave it out.
@@ -1703,13 +1730,17 @@ function Button({ config, devMode }) {
1703
1730
  <!--[if mso]>
1704
1731
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse;">
1705
1732
  <tr>
1706
- <td align="${align}" style="padding: 0;">
1733
+ <td ${align ? `align="${align}"` : ""} style="padding: 0;">
1707
1734
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="${width || "auto"}" style="border-collapse: collapse;">
1708
1735
  <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>
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>`}
1713
1744
  </td>
1714
1745
  </tr>
1715
1746
  </table>
@@ -1723,7 +1754,7 @@ function Button({ config, devMode }) {
1723
1754
  // fontFamily uses the sanitized value so embedded quotes never break the
1724
1755
  // style attribute string (which is always wrapped in double quotes).
1725
1756
  const sharedTextStyles = [
1726
- `color: ${color};`,
1757
+ color ? `color: ${color};` : "",
1727
1758
  safeFontFamily ? `font-family: ${safeFontFamily};` : "",
1728
1759
  fontSize ? `font-size: ${fontSize};` : "",
1729
1760
  fontWeight ? `font-weight: ${fontWeight};` : "",
@@ -1736,7 +1767,7 @@ function Button({ config, devMode }) {
1736
1767
  : "",
1737
1768
  direction ? `direction: ${direction};` : "",
1738
1769
  opacity !== undefined ? `opacity: ${opacity};` : "",
1739
- whiteSpace !== "normal" ? `white-space: ${whiteSpace};` : "",
1770
+ whiteSpace ? `white-space: ${whiteSpace};` : "",
1740
1771
  ]
1741
1772
  .filter(Boolean)
1742
1773
  .join(" ");
@@ -1770,20 +1801,24 @@ function Button({ config, devMode }) {
1770
1801
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse; width: 100%;">
1771
1802
  <tbody>
1772
1803
  <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}">
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}">
1775
1806
  <tbody>
1776
1807
  <tr>
1777
1808
  <td style="padding: 0;">
1778
1809
  ${devMode
1779
- ? `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; word-break: ${wordBreak}; text-align: ${textAlign}; 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};` : ""}">
1780
1811
  ${typeof children === "string" ? children : ""}
1781
1812
  </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>
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};` : ""}">
1784
1820
  ${typeof children === "string" ? children : ""}
1785
- </span>
1786
- </a>`}
1821
+ </span>`}
1787
1822
  </td>
1788
1823
  </tr>
1789
1824
  </tbody>
@@ -2102,40 +2137,48 @@ var Container_default = memo(Container, arePropsEqual);
2102
2137
 
2103
2138
  function Divider({ config, devNode }) {
2104
2139
  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
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
2131
2151
  width: "100%",
2132
2152
  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: {
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).
2134
2176
  height: height,
2177
+ lineHeight: height,
2135
2178
  fontSize: "0",
2136
- lineHeight: "0",
2137
2179
  padding: "0",
2138
- }, 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 }) }) }))] }));
2139
2182
  }
2140
2183
  var Divider_default = memo(Divider, arePropsEqual);
2141
2184
 
@@ -2380,6 +2423,27 @@ function Html({ children, backgroundColor = "#ffffff", }) {
2380
2423
  );
2381
2424
  }
2382
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
+ }
2383
2447
  function getBorderStyle$3(border) {
2384
2448
  if (!border)
2385
2449
  return {};
@@ -2422,7 +2486,10 @@ function getBorderStyleString$1(border) {
2422
2486
  }
2423
2487
  function Image({ config, devNode, devMode }) {
2424
2488
  var _a, _b;
2425
- const { src, alt, href, target, mobile } = config;
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";
2426
2493
  const seed = src + (alt || "");
2427
2494
  const instanceId = seed
2428
2495
  .split("")
@@ -2452,32 +2519,56 @@ function Image({ config, devNode, devMode }) {
2452
2519
  ? parseInt(config.maxWidth, 10)
2453
2520
  : undefined;
2454
2521
  const imgWidthAttr = isPercent ? (maxWidthPx !== null && maxWidthPx !== void 0 ? maxWidthPx : undefined) : widthAttr;
2455
- // 2. Mobile Overrides (Every property used)
2522
+ // 2. Mobile Overrides only emit CSS properties that are explicitly set,
2523
+ // so unspecified properties are left untouched (no forced defaults).
2456
2524
  let mobileCss = "";
2457
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));
2458
2561
  mobileCss = `
2459
2562
  @media screen and (max-width: 768px) {
2460
2563
  .wrap-${imgClass} {
2461
2564
  /* 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;
2565
+ ${wrapRules.join("\n ")}
2465
2566
  }
2466
2567
  .td-${imgClass} {
2467
- padding: ${mobile.padding || "0"} !important;
2468
- background-color: ${mobile.backgroundColor || "transparent"} !important;
2469
- width: 100% !important;
2568
+ ${tdRules.join("\n ")}
2470
2569
  }
2471
2570
  .${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)}
2571
+ ${imgRules.join("\n ")}
2481
2572
  }
2482
2573
  }
2483
2574
  `;
@@ -2495,7 +2586,7 @@ function Image({ config, devNode, devMode }) {
2495
2586
  objectFit: config.objectFit,
2496
2587
  objectPosition: config.objectPosition,
2497
2588
  };
2498
- const imageElement = (jsx("img", { src: src, alt: alt, width: imgWidthAttr, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle }));
2589
+ const imageElement = (jsx("img", { src: src, alt: alt, width: imgWidthAttr, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle, draggable: !devMode }));
2499
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
2500
2591
  , style: {
2501
2592
  width: tableWidth, // Fixed px here prevents the 100% "ghost space"
@@ -2508,7 +2599,9 @@ function Image({ config, devNode, devMode }) {
2508
2599
  fontSize: "0",
2509
2600
  lineHeight: "0",
2510
2601
  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 }) }) }))] })] }));
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 }) }) }))] })] }));
2512
2605
  }
2513
2606
  var Image_default = memo(Image, arePropsEqual);
2514
2607
 
@@ -2582,7 +2675,7 @@ function Row({ children, config, devNode, devMode }) {
2582
2675
  const childrenArray = (Array.isArray(children) ? children : [children]).filter((child) => child != null);
2583
2676
  const numChildren = childrenArray.length;
2584
2677
  const href = getHrefFromInnerLink(config.innerLink);
2585
- 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";
2586
2679
  // 1. Outer TD: Background, Border Radius, Width, Height.
2587
2680
  const backgroundTdStyle = {
2588
2681
  backgroundColor: config.backgroundColor,
@@ -2657,7 +2750,7 @@ function Row({ children, config, devNode, devMode }) {
2657
2750
  }, className: "child-cell", children: child }), index < numChildren - 1 &&
2658
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 }) }) }))] }));
2659
2752
  if (href && !devMode) {
2660
- return (jsx("a", { href: href, ...(target && { target }), style: {
2753
+ return (jsx("a", { href: href, ...({ target }), style: {
2661
2754
  textDecoration: "none",
2662
2755
  color: "inherit",
2663
2756
  display: "block",
@@ -2903,7 +2996,7 @@ function buildIconifyUrl(config) {
2903
2996
  const template = process.env.ICONIFY_API_IMAGE_URI ||
2904
2997
  "https://iconify.pagenflow.com/api/image/{{height}}/{{color}}/{{rotate}}-{{rotate-orientation}}/{{icon-full-name}}.png";
2905
2998
  return template
2906
- .replace("{{height}}", String(numericHeight * 2))
2999
+ .replace("{{height}}", String(numericHeight * 4))
2907
3000
  .replace("{{color}}", cleanColor)
2908
3001
  .replace("{{rotate}}", String(rotate))
2909
3002
  .replace("{{rotate-orientation}}", rotateOrientation)
@@ -2937,7 +3030,7 @@ function Icon({ config, devNode, devMode, children }) {
2937
3030
  // Determine icon source
2938
3031
  const iconSrc = buildIconifyUrl(config);
2939
3032
  const href = buildLinkHref(innerLink);
2940
- 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";
2941
3034
  const align = justifyMap[justifyContent];
2942
3035
  // Get border styles
2943
3036
  const borderStyle = getBorderStyle(border);