@pagenflow/email 1.3.2 → 1.3.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.
@@ -17,6 +17,11 @@ export interface RowConfig {
17
17
  backgroundImage?: BackgroundImageType;
18
18
  borderRadius?: string;
19
19
  border?: BorderConfig;
20
+ mobile?: {
21
+ justifyContent?: JustifyContent;
22
+ alignItems?: AlignItems;
23
+ wrap?: boolean;
24
+ };
20
25
  }
21
26
  export type RowProps = {
22
27
  children: ReactNode;
package/dist/index.cjs.js CHANGED
@@ -1526,7 +1526,7 @@ function arePropsEqual(prevProps, nextProps) {
1526
1526
  }
1527
1527
 
1528
1528
  // Map alignment to HTML 'align' attribute
1529
- const justifyMap$2 = {
1529
+ const justifyMap$3 = {
1530
1530
  start: "left",
1531
1531
  center: "center",
1532
1532
  end: "right",
@@ -1549,7 +1549,7 @@ function Button({ config, devMode }) {
1549
1549
  const vmlFillColor = backgroundColor.startsWith("#")
1550
1550
  ? backgroundColor
1551
1551
  : `#${backgroundColor}`;
1552
- const align = justifyMap$2[justifyContent];
1552
+ const align = justifyMap$3[justifyContent];
1553
1553
  // Build VML font styles
1554
1554
  const vmlFontWeight = fontWeight || "bold";
1555
1555
  const vmlFontStyle = fontStyle === "italic" ? "font-style:italic;" : "";
@@ -1712,7 +1712,7 @@ const alignMap$1 = {
1712
1712
  center: "middle",
1713
1713
  end: "bottom",
1714
1714
  };
1715
- const justifyMap$1 = {
1715
+ const justifyMap$2 = {
1716
1716
  start: "left",
1717
1717
  center: "center",
1718
1718
  end: "right",
@@ -1816,7 +1816,7 @@ function Container({ children, config, devMode, devNode }) {
1816
1816
  fontSize: "1px",
1817
1817
  };
1818
1818
  const justifyAlign = config.justifyContent
1819
- ? justifyMap$1[config.justifyContent]
1819
+ ? justifyMap$2[config.justifyContent]
1820
1820
  : "center";
1821
1821
  const containerWidthAttr = widthType === "fixed" ? containerWidthPx : undefined;
1822
1822
  const isStacking = config.shouldWrap && numChildren > 1;
@@ -1926,14 +1926,14 @@ function Head({ children, backgroundColor = "#ffffff", title = "Email Preview",
1926
1926
  }
1927
1927
  `;
1928
1928
  const globalStyles = `
1929
- @media screen and (max-width: 600px) {
1929
+ @media screen and (max-width: 768px) {
1930
1930
  .container-fixed-width {
1931
1931
  width: 100% !important;
1932
1932
  max-width: 100% !important;
1933
1933
  }
1934
1934
  }
1935
1935
 
1936
- @media screen and (max-width: 600px) {
1936
+ @media screen and (max-width: 768px) {
1937
1937
  .stack-td {
1938
1938
  width: 100% !important;
1939
1939
  display: block !important;
@@ -1956,6 +1956,122 @@ function Head({ children, backgroundColor = "#ffffff", title = "Email Preview",
1956
1956
  mso-line-height-rule: exactly;
1957
1957
  }
1958
1958
  }
1959
+
1960
+ @media only screen and (max-width: 768px) {
1961
+ /* 1. Handling Mobile Alignment (Justify) - Works for both wrapped and non-wrapped */
1962
+ /* We target the inner table alignment */
1963
+ .responsive-row[data-mobile-justify="center"] .content-table {
1964
+ margin: 0 auto !important;
1965
+ float: none !important;
1966
+ }
1967
+ .responsive-row[data-mobile-justify="start"] .content-table {
1968
+ margin: 0 !important;
1969
+ float: left !important;
1970
+ }
1971
+ .responsive-row[data-mobile-justify="end"] .content-table {
1972
+ margin: 0 0 0 auto !important;
1973
+ float: right !important;
1974
+ }
1975
+
1976
+ /* Mobile justify for wrapped children - we need to target the outer wrapper td */
1977
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="center"] td[align] {
1978
+ text-align: center !important;
1979
+ }
1980
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="start"] td[align] {
1981
+ text-align: left !important;
1982
+ }
1983
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="end"] td[align] {
1984
+ text-align: right !important;
1985
+ }
1986
+
1987
+ /* Also apply to child content tables for better support */
1988
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="center"] .child-cell table {
1989
+ margin-left: auto !important;
1990
+ margin-right: auto !important;
1991
+ }
1992
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="start"] .child-cell table {
1993
+ margin-left: 0 !important;
1994
+ margin-right: auto !important;
1995
+ }
1996
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="end"] .child-cell table {
1997
+ margin-left: auto !important;
1998
+ margin-right: 0 !important;
1999
+ }
2000
+
2001
+ /* 2. Handling Mobile Vertical Alignment (Align Items) */
2002
+ /* For non-wrapped rows - controls vertical alignment when cells are side-by-side */
2003
+ .responsive-row[data-mobile-align="center"]:not([data-mobile-wrap="true"]) .child-cell {
2004
+ vertical-align: middle !important;
2005
+ }
2006
+ .responsive-row[data-mobile-align="start"]:not([data-mobile-wrap="true"]) .child-cell {
2007
+ vertical-align: top !important;
2008
+ }
2009
+ .responsive-row[data-mobile-align="end"]:not([data-mobile-wrap="true"]) .child-cell {
2010
+ vertical-align: bottom !important;
2011
+ }
2012
+
2013
+ /* For wrapped rows - alignItems controls vertical alignment of content within each child cell */
2014
+ .responsive-row[data-mobile-wrap="true"][data-mobile-align="center"] .child-cell {
2015
+ vertical-align: middle !important;
2016
+ }
2017
+ .responsive-row[data-mobile-wrap="true"][data-mobile-align="start"] .child-cell {
2018
+ vertical-align: top !important;
2019
+ }
2020
+ .responsive-row[data-mobile-wrap="true"][data-mobile-align="end"] .child-cell {
2021
+ vertical-align: bottom !important;
2022
+ }
2023
+
2024
+ /* 3. Handling Mobile Wrap - Pure CSS Solution */
2025
+ /* Force table to act like block container */
2026
+ .responsive-row[data-mobile-wrap="true"] .content-table {
2027
+ width: 100% !important;
2028
+ max-width: 100% !important;
2029
+ }
2030
+
2031
+ /* Force table row to stack cells */
2032
+ .responsive-row[data-mobile-wrap="true"] .content-tr {
2033
+ display: block !important;
2034
+ }
2035
+
2036
+ /* Force each child cell to be full width block */
2037
+ .responsive-row[data-mobile-wrap="true"] .child-cell {
2038
+ display: block !important;
2039
+ width: 100% !important;
2040
+ box-sizing: border-box !important;
2041
+ }
2042
+
2043
+ /* Hide horizontal gap cells and create vertical spacing with padding */
2044
+ .responsive-row[data-mobile-wrap="true"] .row-gap-td {
2045
+ display: none !important;
2046
+ width: 0 !important;
2047
+ height: 0 !important;
2048
+ }
2049
+
2050
+ /* Add vertical spacing between stacked cells using margin */
2051
+ .responsive-row[data-mobile-wrap="true"] .child-cell:not(:last-child) {
2052
+ margin-bottom: 20px !important;
2053
+ }
2054
+
2055
+ /* Dynamic gap support - common values */
2056
+ .responsive-row[data-mobile-wrap="true"][data-gap="10px"] .child-cell:not(:last-child) {
2057
+ margin-bottom: 10px !important;
2058
+ }
2059
+ .responsive-row[data-mobile-wrap="true"][data-gap="15px"] .child-cell:not(:last-child) {
2060
+ margin-bottom: 15px !important;
2061
+ }
2062
+ .responsive-row[data-mobile-wrap="true"][data-gap="20px"] .child-cell:not(:last-child) {
2063
+ margin-bottom: 20px !important;
2064
+ }
2065
+ .responsive-row[data-mobile-wrap="true"][data-gap="24px"] .child-cell:not(:last-child) {
2066
+ margin-bottom: 24px !important;
2067
+ }
2068
+ .responsive-row[data-mobile-wrap="true"][data-gap="30px"] .child-cell:not(:last-child) {
2069
+ margin-bottom: 30px !important;
2070
+ }
2071
+ .responsive-row[data-mobile-wrap="true"][data-gap="40px"] .child-cell:not(:last-child) {
2072
+ margin-bottom: 40px !important;
2073
+ }
2074
+ }
1959
2075
 
1960
2076
  /* ================================================= */
1961
2077
  /* 🔒 UNIVERSAL LINK RESET */
@@ -2152,7 +2268,7 @@ function Image({ config, devNode, devMode }) {
2152
2268
  }
2153
2269
  var Image_default = React.memo(Image, arePropsEqual);
2154
2270
 
2155
- const justifyMap = {
2271
+ const justifyMap$1 = {
2156
2272
  start: "left",
2157
2273
  center: "center",
2158
2274
  end: "right",
@@ -2184,7 +2300,7 @@ function getBorderStyle$1(border) {
2184
2300
  return style;
2185
2301
  }
2186
2302
  function Row({ children, config, devNode }) {
2187
- var _a, _b, _c;
2303
+ var _a, _b, _c, _d, _e, _f;
2188
2304
  const childrenArray = (Array.isArray(children) ? children : [children]).filter((child) => child != null);
2189
2305
  const numChildren = childrenArray.length;
2190
2306
  // 1. Outer TD for Background and Border Radius (no border here)
@@ -2217,7 +2333,7 @@ function Row({ children, config, devNode }) {
2217
2333
  fontSize: "1px",
2218
2334
  };
2219
2335
  const tdAlign = config.justifyContent
2220
- ? justifyMap[config.justifyContent]
2336
+ ? justifyMap$1[config.justifyContent]
2221
2337
  : "left";
2222
2338
  const tdValign = config.alignItems ? alignMap[config.alignItems] : "top";
2223
2339
  return (jsxRuntime.jsxs("table", Object.assign({ "aria-label": "Row Outer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
@@ -2225,17 +2341,17 @@ function Row({ children, config, devNode }) {
2225
2341
  width: config.width || "100%",
2226
2342
  height: config.height,
2227
2343
  borderCollapse: "collapse",
2228
- } }, (config.height && { height: config.height }), { children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", Object.assign({ style: backgroundTdStyle }, (config.height && { height: config.height }), { children: jsxRuntime.jsx("table", { "aria-label": "Row Border Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: borderTableStyle, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: paddingTdStyle, children: jsxRuntime.jsx("table", { "aria-label": "Row Justification Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2344
+ } }, (config.height && { height: config.height }), { "data-mobile-justify": (_d = config.mobile) === null || _d === void 0 ? void 0 : _d.justifyContent, "data-mobile-align": (_e = config.mobile) === null || _e === void 0 ? void 0 : _e.alignItems, "data-mobile-wrap": ((_f = config.mobile) === null || _f === void 0 ? void 0 : _f.wrap) ? "true" : undefined, "data-gap": config.gap, className: "responsive-row", children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", Object.assign({ style: backgroundTdStyle }, (config.height && { height: config.height }), { children: jsxRuntime.jsx("table", { "aria-label": "Row Border Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: borderTableStyle, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: paddingTdStyle, children: jsxRuntime.jsx("table", { "aria-label": "Row Justification Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2229
2345
  width: "100%",
2230
2346
  height: "100%",
2231
2347
  borderCollapse: "collapse",
2232
- }, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { align: tdAlign, children: jsxRuntime.jsx("table", Object.assign({ "aria-label": "Row Content", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: contentTableStyle }, (config.height && { height: config.height }), { children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: childrenArray.map((child, index) => (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx("td", { valign: tdValign, style: {
2348
+ }, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { align: tdAlign, children: jsxRuntime.jsx("table", Object.assign({ "aria-label": "Row Content", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: contentTableStyle }, (config.height && { height: config.height }), { className: "content-table", children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { className: "content-tr", children: childrenArray.map((child, index) => (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx("td", { valign: tdValign, style: {
2233
2349
  verticalAlign: tdValign,
2234
2350
  textAlign: "left",
2235
2351
  padding: "0",
2236
2352
  margin: "0",
2237
- }, children: child }), index < numChildren - 1 &&
2238
- config.gap && (jsxRuntime.jsx("td", { width: config.gap, style: gapTdStyle, children: "\u00A0" }, `row-gap-${index}`))] }, `row-child-${index}`))) }) }) })) }) }) }) }) }) }) }) }) })) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] })));
2353
+ }, className: "child-cell", children: child }), index < numChildren - 1 &&
2354
+ 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 }) }) }))] })));
2239
2355
  }
2240
2356
  var Row_default = React.memo(Row, arePropsEqual);
2241
2357
 
@@ -2316,7 +2432,6 @@ function Spacer({ config, devNode }) {
2316
2432
  var Spacer_default = React.memo(Spacer, arePropsEqual);
2317
2433
 
2318
2434
  function Text({ config, devMode, children }) {
2319
- var _a;
2320
2435
  const { text, padding, color, textAlign, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, backgroundColor, opacity, whiteSpace, } = config;
2321
2436
  // 1. TD Style: Where padding and background are reliably applied.
2322
2437
  const tdStyle = {
@@ -2325,9 +2440,7 @@ function Text({ config, devMode, children }) {
2325
2440
  width: "100%",
2326
2441
  verticalAlign: "top",
2327
2442
  };
2328
- // 2. Content Style: Applied directly to a wrapper element (like a <div> or <p>)
2329
- // or inherited by the children. For max compatibility, we apply core styles
2330
- // directly to the TD or a wrapper <p> (if children is just a string).
2443
+ // 2. Content Style: Applied directly to a wrapper element
2331
2444
  const contentStyle = {
2332
2445
  color: color,
2333
2446
  textAlign: textAlign,
@@ -2342,19 +2455,188 @@ function Text({ config, devMode, children }) {
2342
2455
  verticalAlign: verticalAlign,
2343
2456
  opacity: opacity,
2344
2457
  whiteSpace: whiteSpace,
2345
- margin: "0", // Crucial: Remove default margin from <p> tags
2458
+ margin: "0",
2346
2459
  padding: "0",
2347
- fontFamily: "Arial, Helvetica, sans-serif", // Use a widely supported font stack
2460
+ fontFamily: "Arial, Helvetica, sans-serif",
2348
2461
  };
2349
- return (
2350
- // Wrap the text content in a table for padding/background/width management.
2351
- jsxRuntime.jsx("table", { "aria-label": "Text Block Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2462
+ // Determine content to render
2463
+ const content = text !== null && text !== void 0 ? text : children;
2464
+ const isString = typeof content === "string";
2465
+ return (jsxRuntime.jsx("table", { "aria-label": "Text Block Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2352
2466
  width: "100%",
2353
2467
  borderCollapse: "collapse",
2354
- }, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: tdStyle, align: textAlign, children: jsxRuntime.jsx("div", { style: contentStyle, dangerouslySetInnerHTML: { __html: (_a = text !== null && text !== void 0 ? text : children) !== null && _a !== void 0 ? _a : "" } }) }) }) }) }));
2468
+ }, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: tdStyle, align: textAlign, children: isString ? (jsxRuntime.jsx("div", { style: contentStyle, dangerouslySetInnerHTML: { __html: content } })) : (jsxRuntime.jsx("div", { style: contentStyle, children: content })) }) }) }) }));
2355
2469
  }
2356
2470
  var Text_default = React.memo(Text, arePropsEqual);
2357
2471
 
2472
+ // Map alignment to HTML 'align' attribute
2473
+ const justifyMap = {
2474
+ start: "left",
2475
+ center: "center",
2476
+ end: "right",
2477
+ };
2478
+ // Helper to build Iconify API URL
2479
+ function buildIconifyUrl(config) {
2480
+ const { iconIdentifier, height = 24, color = "000000", rotate = 0, rotateOrientation = "cw", } = config;
2481
+ if (!iconIdentifier)
2482
+ return null;
2483
+ // Remove # from color if present
2484
+ const cleanColor = color.replace("#", "");
2485
+ // Build URL from template
2486
+ const template = process.env.ICONIFY_API_IMAGE_URI ||
2487
+ "https://iconify.pagenflow.com/api/image/{{height}}/{{color}}/{{rotate}}-{{rotate-orientation}}/{{icon-full-name}}.png";
2488
+ return template
2489
+ .replace("{{height}}", String(Number(height) * Number(2)))
2490
+ .replace("{{color}}", cleanColor)
2491
+ .replace("{{rotate}}", String(rotate))
2492
+ .replace("{{rotate-orientation}}", rotateOrientation)
2493
+ .replace("{{icon-full-name}}", iconIdentifier);
2494
+ }
2495
+ // Helper to build link href based on innerLink type
2496
+ function buildLinkHref(innerLink) {
2497
+ if (!innerLink || innerLink.type === "none")
2498
+ return null;
2499
+ switch (innerLink.type) {
2500
+ case "url":
2501
+ return innerLink.url || null;
2502
+ case "email":
2503
+ return innerLink.email ? `mailto:${innerLink.email}` : null;
2504
+ case "phone":
2505
+ return innerLink.phone ? `tel:${innerLink.phone}` : null;
2506
+ case "anchor":
2507
+ return innerLink.anchor ? `#${innerLink.anchor}` : null;
2508
+ case "page_top":
2509
+ return "#top";
2510
+ case "page_bottom":
2511
+ return "#bottom";
2512
+ default:
2513
+ return null;
2514
+ }
2515
+ }
2516
+ function Icon({ config, devNode, devMode, children }) {
2517
+ const {
2518
+ // base64Source,
2519
+ width, height, backgroundColor, padding = "0", borderRadius = "0", innerLink, justifyContent = "center", } = config;
2520
+ // Determine icon source
2521
+ const iconSrc = buildIconifyUrl(config);
2522
+ const href = buildLinkHref(innerLink);
2523
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_self";
2524
+ const align = justifyMap[justifyContent];
2525
+ // Convert width/height to string with px if number
2526
+ const widthStr = typeof width === "number" ? `${width}px` : width;
2527
+ const heightStr = typeof height === "number" ? `${height}px` : height;
2528
+ // Parse numeric values for HTML attributes
2529
+ const widthNum = typeof width === "number"
2530
+ ? width
2531
+ : typeof width === "string" && width.endsWith("px")
2532
+ ? parseInt(width, 10)
2533
+ : undefined;
2534
+ const heightNum = typeof height === "number"
2535
+ ? height
2536
+ : typeof height === "string" && height.endsWith("px")
2537
+ ? parseInt(height, 10)
2538
+ : undefined;
2539
+ // 1. Image Style: Critical for compatibility
2540
+ const imgStyle = {
2541
+ display: "block", // Prevents extra vertical space
2542
+ border: 0, // No default border
2543
+ width: widthStr || "auto",
2544
+ height: heightStr || "auto",
2545
+ };
2546
+ // 2. Link Style: No underline or color changes
2547
+ const linkStyle = {
2548
+ display: "block",
2549
+ textDecoration: "none",
2550
+ border: 0,
2551
+ outline: "none",
2552
+ };
2553
+ // 3. TD Style: Padding and background
2554
+ const tdStyle = {
2555
+ padding: padding,
2556
+ backgroundColor: backgroundColor,
2557
+ fontSize: "0", // CRITICAL: Collapses extra space
2558
+ lineHeight: "0", // CRITICAL: Collapses extra space7
2559
+ borderRadius: borderRadius,
2560
+ overflow: "hidden",
2561
+ };
2562
+ // --- VML Calculation for Outlook Compatibility ---
2563
+ const numericPadding = parseInt(padding.split(" ")[0] || "0", 10);
2564
+ const vmlWidth = (widthNum || 24) + numericPadding * 2;
2565
+ const vmlHeight = (heightNum || 24) + numericPadding * 2;
2566
+ // VML colors must use full hex format
2567
+ const vmlFillColor = (backgroundColor === null || backgroundColor === void 0 ? void 0 : backgroundColor.startsWith("#"))
2568
+ ? backgroundColor
2569
+ : backgroundColor
2570
+ ? `#${backgroundColor}`
2571
+ : "#ffffff";
2572
+ // Calculate arcsize percentage for VML
2573
+ const numericBorderRadius = parseInt(borderRadius || "0", 10);
2574
+ const arcsize = numericBorderRadius > 0
2575
+ ? Math.min((numericBorderRadius / Math.min(vmlWidth, vmlHeight)) * 100, 100)
2576
+ : 0;
2577
+ // Build VML code for Outlook
2578
+ const vmlIcon = backgroundColor && numericBorderRadius > 0
2579
+ ? `
2580
+ <!--[if mso]>
2581
+ <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" ${href && !devMode ? `href="${href}"` : ""} style="height:${vmlHeight}px;width:${vmlWidth}px;v-text-anchor:middle;" arcsize="${arcsize}%" stroke="false" fillcolor="${vmlFillColor}">
2582
+ <w:anchorlock/>
2583
+ <v:textbox inset="0,0,0,0" style="text-align: center;">
2584
+ <center style="padding:${padding};">
2585
+ <img src="${iconSrc || ""}" alt="" width="${widthNum || 24}" height="${heightNum || 24}" border="0" style="display:block;border:0;" />
2586
+ </center>
2587
+ </v:textbox>
2588
+ </v:roundrect>
2589
+ <![endif]-->
2590
+ `
2591
+ : "";
2592
+ // If no icon source, return empty
2593
+ if (!iconSrc && !devMode) {
2594
+ return null;
2595
+ }
2596
+ // Icon image element
2597
+ const iconElement = devMode && !!children ? (children) : iconSrc ? (jsxRuntime.jsx("img", { draggable: false, src: iconSrc, alt: "" // Icons are decorative, empty alt is appropriate
2598
+ , style: imgStyle, width: widthNum, height: heightNum, border: 0 })) : (jsxRuntime.jsx(jsxRuntime.Fragment, {}));
2599
+ // Wrap in link if href exists and not in dev mode
2600
+ const content = href && !devMode ? (jsxRuntime.jsx("a", Object.assign({ href: href, target: target, style: linkStyle }, (target === "_blank" ? { rel: "noopener noreferrer" } : {}), { children: iconElement }))) : (iconElement);
2601
+ // Build the HTML content with VML support (only when NOT in dev mode)
2602
+ const useVML = !devMode && backgroundColor && numericBorderRadius > 0;
2603
+ const htmlContent = useVML
2604
+ ? `
2605
+ ${vmlIcon}
2606
+ <!--[if !mso]><!-->
2607
+ <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse; width: 100%;">
2608
+ <tbody>
2609
+ <tr>
2610
+ <td style="background-color: ${backgroundColor}; border-radius: ${borderRadius}; padding: ${padding}; font-size: 0; line-height: 0; overflow: hidden;">
2611
+ ${href
2612
+ ? `<a href="${href}" target="${target}" style="display:block;text-decoration:none;border:0;outline:none;" ${target === "_blank" ? 'rel="noopener noreferrer"' : ""}>
2613
+ <img draggable="false" src="${iconSrc}" alt="" width="${widthNum || 24}" height="${heightNum || 24}" border="0" style="display:block;border:0;width:${widthStr || "auto"};height:${heightStr || "auto"};" />
2614
+ </a>`
2615
+ : `<img draggable="false" src="${iconSrc}" alt="" width="${widthNum || 24}" height="${heightNum || 24}" border="0" style="display:block;border:0;width:${widthStr || "auto"};height:${heightStr || "auto"};" />`}
2616
+ </td>
2617
+ </tr>
2618
+ </tbody>
2619
+ </table>
2620
+ <!--<![endif]-->
2621
+ `
2622
+ : null;
2623
+ return (jsxRuntime.jsxs("table", { "aria-label": "Icon", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: align, style: {
2624
+ // --- Start dev
2625
+ position: "relative",
2626
+ // --- End dev
2627
+ width: widthStr || "auto",
2628
+ borderCollapse: "collapse",
2629
+ // base
2630
+ boxSizing: "border-box",
2631
+ border: 0,
2632
+ margin: 0,
2633
+ padding: 0,
2634
+ }, onClick: devMode ? (e) => e.preventDefault() : undefined, children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: useVML ? (jsxRuntime.jsx("td", { dangerouslySetInnerHTML: {
2635
+ __html: htmlContent !== null && htmlContent !== void 0 ? htmlContent : "",
2636
+ } })) : (jsxRuntime.jsx("td", { style: tdStyle, align: align, children: content })) }) }), devMode && !!devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] }));
2637
+ }
2638
+ var Icon_default = React.memo(Icon, arePropsEqual);
2639
+
2358
2640
  exports.Body = Body;
2359
2641
  exports.Button = Button_default;
2360
2642
  exports.Column = Column_default;
@@ -2363,6 +2645,7 @@ exports.Divider = Divider_default;
2363
2645
  exports.Head = Head;
2364
2646
  exports.Heading = Heading_default;
2365
2647
  exports.Html = Html;
2648
+ exports.Icon = Icon_default;
2366
2649
  exports.Image = Image_default;
2367
2650
  exports.Row = Row_default;
2368
2651
  exports.Section = Section_default;