@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.
@@ -1,8 +1,13 @@
1
1
  import { ReactNode } from "react";
2
2
  import { BorderConfig } from "../types";
3
+ import IInnerLink from "../types/IInnerLink";
3
4
  export interface ButtonConfig {
4
- /** The destination URL for the button. Required. */
5
- href: string;
5
+ /** Link configuration for the button destination. Required. */
6
+ innerLink?: IInnerLink;
7
+ /**
8
+ * @deprecated Use innerLink property instead
9
+ */
10
+ href?: string;
6
11
  /** Button text. */
7
12
  children: ReactNode;
8
13
  /** Background color. Required for VML compatibility. */
@@ -19,7 +19,7 @@ export type ColumnConfig = {
19
19
  gap?: string;
20
20
  };
21
21
  export type ColumnProps = {
22
- children: ReactNode;
22
+ children?: ReactNode;
23
23
  config: ColumnConfig;
24
24
  devNode?: ReactNode;
25
25
  };
@@ -37,7 +37,7 @@ export interface ContainerConfig {
37
37
  }
38
38
  export type ContainerProps = {
39
39
  config: ContainerConfig;
40
- children: ReactNode;
40
+ children?: ReactNode;
41
41
  devMode?: boolean;
42
42
  devNode?: ReactNode;
43
43
  };
@@ -1,14 +1,9 @@
1
1
  import { ReactNode } from "react";
2
2
  export interface DividerConfig {
3
- /** Thickness of the line (e.g., "1px"). */
4
3
  height?: string;
5
- /** Color of the line. */
6
4
  color?: string;
7
- /** Width of the line (e.g., "100%" or "300px"). */
8
5
  width?: string;
9
- /** Spacing above and below the divider (e.g., "20px 0"). */
10
6
  margin?: string;
11
- /** Horizontal alignment of the divider. */
12
7
  align?: "left" | "center" | "right";
13
8
  hideOnMobile?: boolean;
14
9
  }
@@ -1,5 +1,11 @@
1
1
  import { CSSProperties, ReactNode } from "react";
2
2
  import { BorderConfig } from "../types";
3
+ import IInnerLink from "../types/IInnerLink";
4
+ /**
5
+ * RULES NOT TO BE REMOVED
6
+ * -------------------
7
+ * - Image should not be draggable in dev mode as we don't want to disrupt the builder dnd behavior
8
+ */
3
9
  export interface ImageMobileConfig {
4
10
  width?: string;
5
11
  height?: string;
@@ -24,7 +30,14 @@ export interface ImageConfig {
24
30
  padding?: string;
25
31
  borderRadius?: string;
26
32
  border?: BorderConfig;
33
+ innerLink?: IInnerLink;
34
+ /**
35
+ * @deprecated Use innerLink property instead
36
+ */
27
37
  href?: string;
38
+ /**
39
+ * @deprecated Use innerLink property instead
40
+ */
28
41
  target?: string;
29
42
  objectFit?: CSSProperties["objectFit"];
30
43
  objectPosition?: string;
@@ -36,7 +36,7 @@ export interface RowConfig {
36
36
  };
37
37
  }
38
38
  export type RowProps = {
39
- children: ReactNode;
39
+ children?: ReactNode;
40
40
  config: RowConfig;
41
41
  devNode?: ReactNode;
42
42
  devMode?: boolean;
@@ -15,7 +15,7 @@ export type SectionConfig = {
15
15
  };
16
16
  export interface SectionProps {
17
17
  config: SectionConfig;
18
- children: ReactNode;
18
+ children?: ReactNode;
19
19
  devNode?: ReactNode;
20
20
  }
21
21
  declare const _default: React.NamedExoticComponent<SectionProps>;
@@ -17,7 +17,7 @@ export interface TextConfig {
17
17
  /** Font style (e.g., 'italic'). */
18
18
  fontStyle?: string;
19
19
  /** Line height (e.g., '1.5' or '24px'). */
20
- lineHeight?: string;
20
+ lineHeight?: string | number;
21
21
  /** Letter spacing (e.g., '0.5px', '1px'). */
22
22
  letterSpacing?: string;
23
23
  /** Text transform (e.g., 'uppercase', 'lowercase', 'capitalize'). */
@@ -37,6 +37,7 @@ export interface TextConfig {
37
37
  /** Word break behavior (e.g., 'break-all', 'break-word', 'keep-all', 'normal'). */
38
38
  wordBreak?: string;
39
39
  maxWidth?: string;
40
+ listStyle?: string;
40
41
  }
41
42
  export type TextProps = {
42
43
  config: TextConfig;
package/dist/index.cjs.js CHANGED
@@ -1539,6 +1539,27 @@ const justifyMap$3 = {
1539
1539
  center: "center",
1540
1540
  end: "right",
1541
1541
  };
1542
+ // Helper to build link href based on innerLink type (mirrors Icon component)
1543
+ function buildLinkHref$2(innerLink) {
1544
+ if (!innerLink || innerLink.type === "none")
1545
+ return null;
1546
+ switch (innerLink.type) {
1547
+ case "url":
1548
+ return innerLink.url || null;
1549
+ case "email":
1550
+ return innerLink.email ? `mailto:${innerLink.email}` : null;
1551
+ case "phone":
1552
+ return innerLink.phone ? `tel:${innerLink.phone}` : null;
1553
+ case "anchor":
1554
+ return innerLink.anchor ? `#${innerLink.anchor}` : null;
1555
+ case "page_top":
1556
+ return "#top";
1557
+ case "page_bottom":
1558
+ return "#bottom";
1559
+ default:
1560
+ return null;
1561
+ }
1562
+ }
1542
1563
  function getBorderStyleString$2(border) {
1543
1564
  if (!border)
1544
1565
  return "";
@@ -1574,8 +1595,10 @@ function getBorderStyleString$2(border) {
1574
1595
  return styles.join(" ");
1575
1596
  }
1576
1597
  function Button({ config, devMode }) {
1577
- const { href, children, backgroundColor = "#007bff", // Default blue
1578
- 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;
1598
+ 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;
1599
+ // Resolve href from innerLink
1600
+ const href = buildLinkHref$2(innerLink);
1601
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
1579
1602
  // Sanitize fontFamily early so safeFontFamily is available for all paths below.
1580
1603
  const safeFontFamily = fontFamily
1581
1604
  ? fontFamily.replace(/['"]/g, "")
@@ -1595,23 +1618,27 @@ function Button({ config, devMode }) {
1595
1618
  // Check if width is percentage-based or not defined
1596
1619
  const isPercentageWidth = !width || width.includes("%");
1597
1620
  const useSimpleOutlookApproach = isPercentageWidth;
1598
- const align = justifyMap$3[justifyContent];
1621
+ const align = justifyContent ? justifyMap$3[justifyContent] : undefined;
1599
1622
  // --- VML Calculation and Code for Outlook Compatibility (Fixed Width Only) ---
1600
1623
  let vmlButton = "";
1601
1624
  if (!useSimpleOutlookApproach) {
1602
1625
  // VML needs fixed pixel height. We estimate it based on padding and potential wrapping.
1603
- const numericPadding = parseInt(padding.split(" ")[0] || "12", 10);
1604
- const numericFontSize = parseInt(fontSize, 10);
1605
- const numericLineHeight = lineHeight.includes("px")
1606
- ? parseInt(lineHeight, 10)
1607
- : numericFontSize * parseFloat(lineHeight);
1626
+ const numericPadding = padding
1627
+ ? parseInt(padding.split(" ")[0] || "12", 10)
1628
+ : 12;
1629
+ const numericFontSize = fontSize ? parseInt(fontSize, 10) : 0;
1630
+ const numericLineHeight = lineHeight
1631
+ ? lineHeight.includes("px")
1632
+ ? parseInt(lineHeight, 10)
1633
+ : numericFontSize * parseFloat(lineHeight)
1634
+ : numericFontSize;
1608
1635
  // Trust user's explicit pixel width - no calculation needed
1609
1636
  const vmlWidth = parseInt(width, 10);
1610
1637
  // Calculate VML height - trust user's padding and let text wrap naturally
1611
1638
  // VML v:textbox will handle text wrapping automatically
1612
1639
  const textContent = typeof children === "string" ? children : "";
1613
1640
  // Estimate number of lines based on text length and button width
1614
- const horizontalPadding = padding.split(" ")[1]
1641
+ const horizontalPadding = (padding === null || padding === void 0 ? void 0 : padding.split(" ")[1])
1615
1642
  ? parseInt(padding.split(" ")[1], 10) * 2
1616
1643
  : numericPadding * 2;
1617
1644
  const availableTextWidth = vmlWidth - horizontalPadding;
@@ -1624,15 +1651,17 @@ function Button({ config, devMode }) {
1624
1651
  // Add extra 4px buffer to prevent bottom cropping in VML
1625
1652
  const vmlHeight = Math.max(numericPadding * 2 + textHeight + 4, 40);
1626
1653
  // VML colors must use the full hex format (e.g., #000000)
1627
- const vmlFillColor = backgroundColor.startsWith("#")
1628
- ? backgroundColor
1629
- : `#${backgroundColor}`;
1654
+ const vmlFillColor = backgroundColor
1655
+ ? backgroundColor.startsWith("#")
1656
+ ? backgroundColor
1657
+ : `#${backgroundColor}`
1658
+ : undefined;
1630
1659
  // VML stroke color for border
1631
1660
  const vmlStrokeColor = (border === null || border === void 0 ? void 0 : border.color) || vmlFillColor;
1632
1661
  const vmlStrokeWeight = (border === null || border === void 0 ? void 0 : border.width) ? parseInt(border.width, 10) : 0;
1633
1662
  const hasVmlStroke = vmlStrokeWeight > 0;
1634
1663
  // Build VML font styles - consistent with other rendering paths
1635
- const vmlFontWeight = fontWeight || "500";
1664
+ const vmlFontWeight = fontWeight;
1636
1665
  const vmlFontStyle = fontStyle === "italic" ? "font-style:italic;" : "";
1637
1666
  const vmlLetterSpacing = letterSpacing
1638
1667
  ? `letter-spacing:${letterSpacing};`
@@ -1643,12 +1672,12 @@ function Button({ config, devMode }) {
1643
1672
  const vmlTextDecoration = textDecoration && textDecoration !== "none"
1644
1673
  ? `text-decoration:${textDecoration};`
1645
1674
  : "";
1646
- const vmlWhiteSpace = whiteSpace !== "normal" ? `white-space:${whiteSpace};` : "";
1675
+ const vmlWhiteSpace = whiteSpace ? `white-space:${whiteSpace};` : "";
1647
1676
  const vmlDirection = direction ? `direction:${direction};` : "";
1648
1677
  const vmlOpacity = opacity !== undefined ? `opacity:${opacity};` : "";
1649
1678
  // VML code uses MSO conditional comments to render only in Outlook
1650
1679
  // Use table with explicit MSO height for vertical centering
1651
- const horizontalPaddingValue = padding.split(" ")[1]
1680
+ const horizontalPaddingValue = (padding === null || padding === void 0 ? void 0 : padding.split(" ")[1])
1652
1681
  ? parseInt(padding.split(" ")[1], 10)
1653
1682
  : numericPadding;
1654
1683
  // For VML, we need to use a table inside to properly apply padding and centering
@@ -1657,7 +1686,7 @@ function Button({ config, devMode }) {
1657
1686
  if (textAlign === "center") {
1658
1687
  vmlAlignAttr = 'align="center"';
1659
1688
  }
1660
- else {
1689
+ else if (textAlign) {
1661
1690
  vmlAlignStyle = `text-align:${textAlign};`;
1662
1691
  }
1663
1692
  // Border radius is intentionally omitted (arcsize="0%") for Outlook Classic.
@@ -1665,12 +1694,12 @@ function Button({ config, devMode }) {
1665
1694
  // is inconsistent, so we render sharp corners there instead.
1666
1695
  vmlButton = `
1667
1696
  <!--[if mso]>
1668
- <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}">
1697
+ <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}"` : ""}>
1669
1698
  <w:anchorlock/>
1670
1699
  <v:textbox inset="${horizontalPaddingValue}px,${numericPadding}px,${horizontalPaddingValue}px,${numericPadding}px">
1671
1700
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse:collapse;">
1672
1701
  <tr>
1673
- <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;">
1702
+ <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;">
1674
1703
  ${typeof children === "string" ? children : ""}
1675
1704
  </td>
1676
1705
  </tr>
@@ -1694,10 +1723,10 @@ function Button({ config, devMode }) {
1694
1723
  const textTransformProp = textTransform
1695
1724
  ? `text-transform: ${textTransform};`
1696
1725
  : "";
1697
- const whiteSpaceProp = whiteSpace !== "normal" ? `white-space: ${whiteSpace};` : "";
1726
+ const whiteSpaceProp = whiteSpace ? `white-space: ${whiteSpace};` : "";
1698
1727
  const directionProp = direction ? `direction: ${direction};` : "";
1699
1728
  const opacityProp = opacity !== undefined ? `opacity: ${opacity};` : "";
1700
- const wordBreakProp = wordBreak !== "break-word" ? `word-break: ${wordBreak};` : "";
1729
+ const wordBreakProp = wordBreak ? `word-break: ${wordBreak};` : "";
1701
1730
  // Border radius is intentionally omitted from the Outlook Classic table cell.
1702
1731
  // Outlook Classic ignores border-radius on table cells anyway, and including it
1703
1732
  // can cause unexpected rendering artifacts, so we explicitly leave it out.
@@ -1705,13 +1734,17 @@ function Button({ config, devMode }) {
1705
1734
  <!--[if mso]>
1706
1735
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse;">
1707
1736
  <tr>
1708
- <td align="${align}" style="padding: 0;">
1737
+ <td ${align ? `align="${align}"` : ""} style="padding: 0;">
1709
1738
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="${width || "auto"}" style="border-collapse: collapse;">
1710
1739
  <tr>
1711
- <td bgcolor="${backgroundColor}" align="${textAlign}" style="padding: ${padding}; text-align: ${textAlign}; ${borderStyleString}">
1712
- <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;">
1713
- ${typeof children === "string" ? children : ""}
1714
- </a>
1740
+ <td ${backgroundColor ? `bgcolor="${backgroundColor}"` : ""} ${textAlign ? `align="${textAlign}"` : ""} style="${padding ? `padding: ${padding};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${borderStyleString}">
1741
+ ${href
1742
+ ? `<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;">
1743
+ ${typeof children === "string" ? children : ""}
1744
+ </a>`
1745
+ : `<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;">
1746
+ ${typeof children === "string" ? children : ""}
1747
+ </span>`}
1715
1748
  </td>
1716
1749
  </tr>
1717
1750
  </table>
@@ -1725,7 +1758,7 @@ function Button({ config, devMode }) {
1725
1758
  // fontFamily uses the sanitized value so embedded quotes never break the
1726
1759
  // style attribute string (which is always wrapped in double quotes).
1727
1760
  const sharedTextStyles = [
1728
- `color: ${color};`,
1761
+ color ? `color: ${color};` : "",
1729
1762
  safeFontFamily ? `font-family: ${safeFontFamily};` : "",
1730
1763
  fontSize ? `font-size: ${fontSize};` : "",
1731
1764
  fontWeight ? `font-weight: ${fontWeight};` : "",
@@ -1738,7 +1771,7 @@ function Button({ config, devMode }) {
1738
1771
  : "",
1739
1772
  direction ? `direction: ${direction};` : "",
1740
1773
  opacity !== undefined ? `opacity: ${opacity};` : "",
1741
- whiteSpace !== "normal" ? `white-space: ${whiteSpace};` : "",
1774
+ whiteSpace ? `white-space: ${whiteSpace};` : "",
1742
1775
  ]
1743
1776
  .filter(Boolean)
1744
1777
  .join(" ");
@@ -1772,20 +1805,24 @@ function Button({ config, devMode }) {
1772
1805
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse; width: 100%;">
1773
1806
  <tbody>
1774
1807
  <tr>
1775
- <td style="background-color: ${backgroundTdStyle.backgroundColor}; border-radius: ${backgroundTdStyle.borderRadius}; width: ${backgroundTdStyle.width}; ${maxWidth ? `max-width: ${maxWidth};` : ""} ${borderRadius ? "overflow: hidden;" : ""}">
1776
- <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: separate; border-spacing: 0; border-radius: ${borderRadius}; width: 100%; ${borderStyleString}">
1808
+ <td style="${backgroundTdStyle.backgroundColor ? `background-color: ${backgroundTdStyle.backgroundColor};` : ""} ${backgroundTdStyle.borderRadius ? `border-radius: ${backgroundTdStyle.borderRadius};` : ""} width: ${backgroundTdStyle.width}; ${maxWidth ? `max-width: ${maxWidth};` : ""} ${borderRadius ? "overflow: hidden;" : ""}">
1809
+ <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: separate; border-spacing: 0; ${borderRadius ? `border-radius: ${borderRadius};` : ""} width: 100%; ${borderStyleString}">
1777
1810
  <tbody>
1778
1811
  <tr>
1779
1812
  <td style="padding: 0;">
1780
1813
  ${devMode
1781
- ? `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; word-break: ${wordBreak}; text-align: ${textAlign}; padding: ${padding};">
1814
+ ? `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; ${wordBreak ? `word-break: ${wordBreak};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${padding ? `padding: ${padding};` : ""}">
1782
1815
  ${typeof children === "string" ? children : ""}
1783
1816
  </span>`
1784
- : `<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};">
1785
- <span>
1817
+ : href
1818
+ ? `<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};` : ""}">
1819
+ <span>
1820
+ ${typeof children === "string" ? children : ""}
1821
+ </span>
1822
+ </a>`
1823
+ : `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; ${wordBreak ? `word-break: ${wordBreak};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${padding ? `padding: ${padding};` : ""}">
1786
1824
  ${typeof children === "string" ? children : ""}
1787
- </span>
1788
- </a>`}
1825
+ </span>`}
1789
1826
  </td>
1790
1827
  </tr>
1791
1828
  </tbody>
@@ -2104,40 +2141,48 @@ var Container_default = React.memo(Container, arePropsEqual);
2104
2141
 
2105
2142
  function Divider({ config, devNode }) {
2106
2143
  const { height = "1px", color = "#cccccc", width = "100%", margin = "20px 0", align = "center", hideOnMobile, } = config;
2107
- // 1. Outer TD Style: Applies the vertical spacing (margin)
2108
- const outerTdStyle = {
2109
- padding: margin,
2110
- fontSize: "0",
2111
- lineHeight: "0",
2112
- width: "100%",
2113
- };
2114
- // 2. Divider Table Style: Applies the line properties
2115
- const dividerTableStyle = {
2116
- width: width,
2117
- height: height,
2118
- backgroundColor: color,
2119
- borderCollapse: "collapse",
2120
- border: "0",
2121
- // ✅ FIX 1: Use string literal indexing for MSO properties
2122
- // ["mso-table-lspace" as string]: "0pt",
2123
- ["msoTableLspace"]: "0pt",
2124
- // ["mso-table-rspace" as string]: "0pt",
2125
- ["msoTableRspace"]: "0pt",
2126
- };
2127
- // Parse height for the HTML attribute
2128
- const dividerHeightAttribute = parseInt(height, 10) || 1;
2129
- return (jsxRuntime.jsxs("table", { "aria-label": "Divider Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2130
- // --- Start dev
2131
- position: "relative",
2132
- // --- End dev
2144
+ const heightPx = parseInt(height, 10) || 1;
2145
+ // Parse margin into paddingTop / paddingBottom for the outer TD.
2146
+ // Outlook ignores shorthand "20px 0" on TDs — must be explicit.
2147
+ const [marginTopRaw = "0", marginRightRaw = "0", marginBottomRaw, marginLeftRaw] = margin.trim().split(/\s+/);
2148
+ const marginTop = marginTopRaw;
2149
+ const marginBottom = marginBottomRaw !== null && marginBottomRaw !== void 0 ? marginBottomRaw : marginTopRaw; // "20px 0" → top=20px, bottom=20px
2150
+ // Outlook requires align on the outer TD to correctly position
2151
+ // a fixed-width inner table (e.g. width="300px").
2152
+ const alignAttr = align === "left" ? "left" : align === "right" ? "right" : "center";
2153
+ return (jsxRuntime.jsxs("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2154
+ position: "relative", // dev overlay anchor
2133
2155
  width: "100%",
2134
2156
  borderCollapse: "collapse",
2135
- }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: outerTdStyle, align: align, children: jsxRuntime.jsx("table", { "aria-label": "Divider Line", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: align, style: dividerTableStyle, ...{ height: dividerHeightAttribute }, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: {
2157
+ border: "0",
2158
+ }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { align: alignAttr, style: {
2159
+ paddingTop: marginTop,
2160
+ paddingBottom: marginBottom,
2161
+ paddingLeft: "0",
2162
+ paddingRight: "0",
2163
+ fontSize: "0",
2164
+ lineHeight: "0",
2165
+ }, children: jsxRuntime.jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: alignAttr, style: {
2166
+ width: width,
2167
+ borderCollapse: "collapse",
2168
+ border: "0",
2169
+ }, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { ...{ height: heightPx }, ref: (el) => {
2170
+ if (!el)
2171
+ return;
2172
+ el.setAttribute("style", `height:${height};` +
2173
+ `line-height:${height};` +
2174
+ `font-size:0;` +
2175
+ `padding:0;` +
2176
+ `background-color:${color};` +
2177
+ `mso-line-height-rule:exactly;`);
2178
+ }, style: {
2179
+ // Fallback for non-Outlook clients (React-rendered style object).
2136
2180
  height: height,
2181
+ lineHeight: height,
2137
2182
  fontSize: "0",
2138
- lineHeight: "0",
2139
2183
  padding: "0",
2140
- }, children: "\u00A0" }) }) }) }) }) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] }));
2184
+ backgroundColor: color,
2185
+ } }) }) }) }) }) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] }));
2141
2186
  }
2142
2187
  var Divider_default = React.memo(Divider, arePropsEqual);
2143
2188
 
@@ -2382,6 +2427,27 @@ function Html({ children, backgroundColor = "#ffffff", }) {
2382
2427
  );
2383
2428
  }
2384
2429
 
2430
+ // Helper to build link href based on innerLink type
2431
+ function buildLinkHref$1(innerLink) {
2432
+ if (!innerLink || innerLink.type === "none")
2433
+ return null;
2434
+ switch (innerLink.type) {
2435
+ case "url":
2436
+ return innerLink.url || null;
2437
+ case "email":
2438
+ return innerLink.email ? `mailto:${innerLink.email}` : null;
2439
+ case "phone":
2440
+ return innerLink.phone ? `tel:${innerLink.phone}` : null;
2441
+ case "anchor":
2442
+ return innerLink.anchor ? `#${innerLink.anchor}` : null;
2443
+ case "page_top":
2444
+ return "#top";
2445
+ case "page_bottom":
2446
+ return "#bottom";
2447
+ default:
2448
+ return null;
2449
+ }
2450
+ }
2385
2451
  function getBorderStyle$3(border) {
2386
2452
  if (!border)
2387
2453
  return {};
@@ -2424,7 +2490,10 @@ function getBorderStyleString$1(border) {
2424
2490
  }
2425
2491
  function Image({ config, devNode, devMode }) {
2426
2492
  var _a, _b;
2427
- const { src, alt, href, target, mobile } = config;
2493
+ const { src, alt, innerLink, mobile } = config;
2494
+ // Resolve href and target from innerLink
2495
+ const href = buildLinkHref$1(innerLink);
2496
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
2428
2497
  const seed = src + (alt || "");
2429
2498
  const instanceId = seed
2430
2499
  .split("")
@@ -2454,32 +2523,56 @@ function Image({ config, devNode, devMode }) {
2454
2523
  ? parseInt(config.maxWidth, 10)
2455
2524
  : undefined;
2456
2525
  const imgWidthAttr = isPercent ? (maxWidthPx !== null && maxWidthPx !== void 0 ? maxWidthPx : undefined) : widthAttr;
2457
- // 2. Mobile Overrides (Every property used)
2526
+ // 2. Mobile Overrides only emit CSS properties that are explicitly set,
2527
+ // so unspecified properties are left untouched (no forced defaults).
2458
2528
  let mobileCss = "";
2459
2529
  if (mobile) {
2530
+ // .wrap-${imgClass} rules
2531
+ const wrapRules = [
2532
+ // Always reset min-width so the px lock from desktop can be overridden
2533
+ "min-width: 0 !important;",
2534
+ ];
2535
+ if (mobile.width !== undefined)
2536
+ wrapRules.push(`width: ${mobile.width} !important;`);
2537
+ if (mobile.maxWidth !== undefined)
2538
+ wrapRules.push(`max-width: ${mobile.maxWidth} !important;`);
2539
+ // .td-${imgClass} rules
2540
+ const tdRules = [];
2541
+ if (mobile.padding !== undefined)
2542
+ tdRules.push(`padding: ${mobile.padding} !important;`);
2543
+ if (mobile.backgroundColor !== undefined)
2544
+ tdRules.push(`background-color: ${mobile.backgroundColor} !important;`);
2545
+ // .${imgClass} rules
2546
+ const imgRules = [];
2547
+ if (mobile.width !== undefined)
2548
+ imgRules.push(`width: ${mobile.width} !important;`);
2549
+ if (mobile.height !== undefined)
2550
+ imgRules.push(`height: ${mobile.height} !important;`);
2551
+ if (mobile.maxWidth !== undefined)
2552
+ imgRules.push(`max-width: ${mobile.maxWidth} !important;`);
2553
+ if (mobile.maxHeight !== undefined)
2554
+ imgRules.push(`max-height: ${mobile.maxHeight} !important;`);
2555
+ if (mobile.borderRadius !== undefined)
2556
+ imgRules.push(`border-radius: ${mobile.borderRadius} !important;`);
2557
+ if (mobile.hidden !== undefined)
2558
+ imgRules.push(`display: ${mobile.hidden ? "none" : "block"} !important;`);
2559
+ if (mobile.objectFit !== undefined)
2560
+ imgRules.push(`object-fit: ${mobile.objectFit} !important;`);
2561
+ if (mobile.objectPosition !== undefined)
2562
+ imgRules.push(`object-position: ${mobile.objectPosition} !important;`);
2563
+ if (mobile.border !== undefined)
2564
+ imgRules.push(getBorderStyleString$1(mobile.border));
2460
2565
  mobileCss = `
2461
2566
  @media screen and (max-width: 768px) {
2462
2567
  .wrap-${imgClass} {
2463
2568
  /* This breaks the px lock from desktop and makes it fluid */
2464
- width: ${mobile.width || "100%"} !important;
2465
- max-width: ${mobile.maxWidth || "100%"} !important;
2466
- min-width: 0 !important;
2569
+ ${wrapRules.join("\n ")}
2467
2570
  }
2468
2571
  .td-${imgClass} {
2469
- padding: ${mobile.padding || "0"} !important;
2470
- background-color: ${mobile.backgroundColor || "transparent"} !important;
2471
- width: 100% !important;
2572
+ ${tdRules.join("\n ")}
2472
2573
  }
2473
2574
  .${imgClass} {
2474
- width: ${mobile.width || "100%"} !important;
2475
- height: ${mobile.height || "auto"} !important;
2476
- max-width: ${mobile.maxWidth || "100%"} !important;
2477
- max-height: ${mobile.maxHeight || "none"} !important;
2478
- border-radius: ${mobile.borderRadius || "0"} !important;
2479
- display: ${mobile.hidden ? "none" : "block"} !important;
2480
- object-fit: ${mobile.objectFit || "fill"} !important;
2481
- object-position: ${mobile.objectPosition || "center"} !important;
2482
- ${getBorderStyleString$1(mobile.border)}
2575
+ ${imgRules.join("\n ")}
2483
2576
  }
2484
2577
  }
2485
2578
  `;
@@ -2497,7 +2590,7 @@ function Image({ config, devNode, devMode }) {
2497
2590
  objectFit: config.objectFit,
2498
2591
  objectPosition: config.objectPosition,
2499
2592
  };
2500
- const imageElement = (jsxRuntime.jsx("img", { src: src, alt: alt, width: imgWidthAttr, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle }));
2593
+ const imageElement = (jsxRuntime.jsx("img", { src: src, alt: alt, width: imgWidthAttr, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle, draggable: !devMode }));
2501
2594
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [mobile && jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: mobileCss } }), jsxRuntime.jsxs("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, className: `wrap-${imgClass}`, align: "center" // Ensures a 300px image stays centered in its parent
2502
2595
  , style: {
2503
2596
  width: tableWidth, // Fixed px here prevents the 100% "ghost space"
@@ -2510,7 +2603,9 @@ function Image({ config, devNode, devMode }) {
2510
2603
  fontSize: "0",
2511
2604
  lineHeight: "0",
2512
2605
  width: tableWidth, // Lock the cell as well
2513
- }, children: href && !devMode ? (jsxRuntime.jsx("a", { href: href, target: target, style: { display: "block", width: "100%" }, children: imageElement })) : (imageElement) }) }) }), devMode && !!devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] })] }));
2606
+ }, children: href && !devMode ? (jsxRuntime.jsx("a", { href: href, target: target, ...(target === "_blank"
2607
+ ? { rel: "noopener noreferrer" }
2608
+ : {}), style: { display: "block", width: "100%" }, children: imageElement })) : (imageElement) }) }) }), devMode && !!devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] })] }));
2514
2609
  }
2515
2610
  var Image_default = React.memo(Image, arePropsEqual);
2516
2611
 
@@ -2584,7 +2679,7 @@ function Row({ children, config, devNode, devMode }) {
2584
2679
  const childrenArray = (Array.isArray(children) ? children : [children]).filter((child) => child != null);
2585
2680
  const numChildren = childrenArray.length;
2586
2681
  const href = getHrefFromInnerLink(config.innerLink);
2587
- const target = (_a = config.innerLink) === null || _a === void 0 ? void 0 : _a.target;
2682
+ const target = ((_a = config.innerLink) === null || _a === void 0 ? void 0 : _a.target) || "_blank";
2588
2683
  // 1. Outer TD: Background, Border Radius, Width, Height.
2589
2684
  const backgroundTdStyle = {
2590
2685
  backgroundColor: config.backgroundColor,
@@ -2659,7 +2754,7 @@ function Row({ children, config, devNode, devMode }) {
2659
2754
  }, className: "child-cell", children: child }), index < numChildren - 1 &&
2660
2755
  config.gap && (jsxRuntime.jsx("td", { width: config.gap, style: gapTdStyle, className: "row-gap-td", children: "\u00A0" }, `row-gap-${index}`))] }, `row-child-${index}`))) }) }) }) }) }) }) }) }) }) }) }) }) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] }));
2661
2756
  if (href && !devMode) {
2662
- return (jsxRuntime.jsx("a", { href: href, ...(target && { target }), style: {
2757
+ return (jsxRuntime.jsx("a", { href: href, ...({ target }), style: {
2663
2758
  textDecoration: "none",
2664
2759
  color: "inherit",
2665
2760
  display: "block",
@@ -2771,7 +2866,7 @@ function Spacer({ config, devNode }) {
2771
2866
  var Spacer_default = React.memo(Spacer, arePropsEqual);
2772
2867
 
2773
2868
  function Text({ config, devMode, children }) {
2774
- const { text, padding, color, textAlign, fontFamily, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, backgroundColor, opacity, whiteSpace, wordBreak = "break-all", maxWidth } = config;
2869
+ const { text, padding, color, textAlign, fontFamily, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, backgroundColor, opacity, whiteSpace, wordBreak = "break-all", maxWidth, } = config;
2775
2870
  // 1. TD Style: Where padding and background are reliably applied.
2776
2871
  const tdStyle = {
2777
2872
  padding: padding,
@@ -2798,7 +2893,7 @@ function Text({ config, devMode, children }) {
2798
2893
  wordBreak: wordBreak,
2799
2894
  margin: "0",
2800
2895
  padding: "0",
2801
- maxWidth
2896
+ maxWidth,
2802
2897
  };
2803
2898
  // Determine content to render
2804
2899
  const content = text !== null && text !== void 0 ? text : children;
@@ -2905,7 +3000,7 @@ function buildIconifyUrl(config) {
2905
3000
  const template = process.env.ICONIFY_API_IMAGE_URI ||
2906
3001
  "https://iconify.pagenflow.com/api/image/{{height}}/{{color}}/{{rotate}}-{{rotate-orientation}}/{{icon-full-name}}.png";
2907
3002
  return template
2908
- .replace("{{height}}", String(numericHeight * 2))
3003
+ .replace("{{height}}", String(numericHeight * 4))
2909
3004
  .replace("{{color}}", cleanColor)
2910
3005
  .replace("{{rotate}}", String(rotate))
2911
3006
  .replace("{{rotate-orientation}}", rotateOrientation)
@@ -2939,7 +3034,7 @@ function Icon({ config, devNode, devMode, children }) {
2939
3034
  // Determine icon source
2940
3035
  const iconSrc = buildIconifyUrl(config);
2941
3036
  const href = buildLinkHref(innerLink);
2942
- const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_self";
3037
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
2943
3038
  const align = justifyMap[justifyContent];
2944
3039
  // Get border styles
2945
3040
  const borderStyle = getBorderStyle(border);