@pagenflow/email 1.3.3 → 1.3.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.d.ts CHANGED
@@ -24,4 +24,6 @@ export { default as Spacer } from './components/Spacer';
24
24
  export type { SpacerProps, SpacerConfig } from './components/Spacer';
25
25
  export { default as Text } from './components/Text';
26
26
  export type { TextProps, TextConfig } from './components/Text';
27
+ export { default as Icon } from './components/Icon';
28
+ export type { IconProps, IconConfig } from './components/Icon';
27
29
  export * from "./types";
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
2
  import React, { memo, Fragment } from 'react';
3
3
 
4
4
  function Body({ children, config = {} }) {
@@ -1524,7 +1524,7 @@ function arePropsEqual(prevProps, nextProps) {
1524
1524
  }
1525
1525
 
1526
1526
  // Map alignment to HTML 'align' attribute
1527
- const justifyMap$2 = {
1527
+ const justifyMap$3 = {
1528
1528
  start: "left",
1529
1529
  center: "center",
1530
1530
  end: "right",
@@ -1547,7 +1547,7 @@ function Button({ config, devMode }) {
1547
1547
  const vmlFillColor = backgroundColor.startsWith("#")
1548
1548
  ? backgroundColor
1549
1549
  : `#${backgroundColor}`;
1550
- const align = justifyMap$2[justifyContent];
1550
+ const align = justifyMap$3[justifyContent];
1551
1551
  // Build VML font styles
1552
1552
  const vmlFontWeight = fontWeight || "bold";
1553
1553
  const vmlFontStyle = fontStyle === "italic" ? "font-style:italic;" : "";
@@ -1710,7 +1710,7 @@ const alignMap$1 = {
1710
1710
  center: "middle",
1711
1711
  end: "bottom",
1712
1712
  };
1713
- const justifyMap$1 = {
1713
+ const justifyMap$2 = {
1714
1714
  start: "left",
1715
1715
  center: "center",
1716
1716
  end: "right",
@@ -1814,7 +1814,7 @@ function Container({ children, config, devMode, devNode }) {
1814
1814
  fontSize: "1px",
1815
1815
  };
1816
1816
  const justifyAlign = config.justifyContent
1817
- ? justifyMap$1[config.justifyContent]
1817
+ ? justifyMap$2[config.justifyContent]
1818
1818
  : "center";
1819
1819
  const containerWidthAttr = widthType === "fixed" ? containerWidthPx : undefined;
1820
1820
  const isStacking = config.shouldWrap && numChildren > 1;
@@ -1924,14 +1924,14 @@ function Head({ children, backgroundColor = "#ffffff", title = "Email Preview",
1924
1924
  }
1925
1925
  `;
1926
1926
  const globalStyles = `
1927
- @media screen and (max-width: 600px) {
1927
+ @media screen and (max-width: 768px) {
1928
1928
  .container-fixed-width {
1929
1929
  width: 100% !important;
1930
1930
  max-width: 100% !important;
1931
1931
  }
1932
1932
  }
1933
1933
 
1934
- @media screen and (max-width: 600px) {
1934
+ @media screen and (max-width: 768px) {
1935
1935
  .stack-td {
1936
1936
  width: 100% !important;
1937
1937
  display: block !important;
@@ -1955,8 +1955,8 @@ function Head({ children, backgroundColor = "#ffffff", title = "Email Preview",
1955
1955
  }
1956
1956
  }
1957
1957
 
1958
- @media only screen and (max-width: 600px) {
1959
- /* 1. Handling Mobile Alignment (Justify) */
1958
+ @media only screen and (max-width: 768px) {
1959
+ /* 1. Handling Mobile Alignment (Justify) - Works for both wrapped and non-wrapped */
1960
1960
  /* We target the inner table alignment */
1961
1961
  .responsive-row[data-mobile-justify="center"] .content-table {
1962
1962
  margin: 0 auto !important;
@@ -1971,17 +1971,104 @@ function Head({ children, backgroundColor = "#ffffff", title = "Email Preview",
1971
1971
  float: right !important;
1972
1972
  }
1973
1973
 
1974
+ /* Mobile justify for wrapped children - we need to target the outer wrapper td */
1975
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="center"] td[align] {
1976
+ text-align: center !important;
1977
+ }
1978
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="start"] td[align] {
1979
+ text-align: left !important;
1980
+ }
1981
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="end"] td[align] {
1982
+ text-align: right !important;
1983
+ }
1984
+
1985
+ /* Also apply to child content tables for better support */
1986
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="center"] .child-cell table {
1987
+ margin-left: auto !important;
1988
+ margin-right: auto !important;
1989
+ }
1990
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="start"] .child-cell table {
1991
+ margin-left: 0 !important;
1992
+ margin-right: auto !important;
1993
+ }
1994
+ .responsive-row[data-mobile-wrap="true"][data-mobile-justify="end"] .child-cell table {
1995
+ margin-left: auto !important;
1996
+ margin-right: 0 !important;
1997
+ }
1998
+
1974
1999
  /* 2. Handling Mobile Vertical Alignment (Align Items) */
1975
- /* We target the child cells if they are still side-by-side */
1976
- .responsive-row[data-mobile-align="center"] .child-cell {
2000
+ /* For non-wrapped rows - controls vertical alignment when cells are side-by-side */
2001
+ .responsive-row[data-mobile-align="center"]:not([data-mobile-wrap="true"]) .child-cell {
2002
+ vertical-align: middle !important;
2003
+ }
2004
+ .responsive-row[data-mobile-align="start"]:not([data-mobile-wrap="true"]) .child-cell {
2005
+ vertical-align: top !important;
2006
+ }
2007
+ .responsive-row[data-mobile-align="end"]:not([data-mobile-wrap="true"]) .child-cell {
2008
+ vertical-align: bottom !important;
2009
+ }
2010
+
2011
+ /* For wrapped rows - alignItems controls vertical alignment of content within each child cell */
2012
+ .responsive-row[data-mobile-wrap="true"][data-mobile-align="center"] .child-cell {
1977
2013
  vertical-align: middle !important;
1978
2014
  }
1979
- .responsive-row[data-mobile-align="start"] .child-cell {
2015
+ .responsive-row[data-mobile-wrap="true"][data-mobile-align="start"] .child-cell {
1980
2016
  vertical-align: top !important;
1981
2017
  }
1982
- .responsive-row[data-mobile-align="end"] .child-cell {
2018
+ .responsive-row[data-mobile-wrap="true"][data-mobile-align="end"] .child-cell {
1983
2019
  vertical-align: bottom !important;
1984
2020
  }
2021
+
2022
+ /* 3. Handling Mobile Wrap - Pure CSS Solution */
2023
+ /* Force table to act like block container */
2024
+ .responsive-row[data-mobile-wrap="true"] .content-table {
2025
+ width: 100% !important;
2026
+ max-width: 100% !important;
2027
+ }
2028
+
2029
+ /* Force table row to stack cells */
2030
+ .responsive-row[data-mobile-wrap="true"] .content-tr {
2031
+ display: block !important;
2032
+ }
2033
+
2034
+ /* Force each child cell to be full width block */
2035
+ .responsive-row[data-mobile-wrap="true"] .child-cell {
2036
+ display: block !important;
2037
+ width: 100% !important;
2038
+ box-sizing: border-box !important;
2039
+ }
2040
+
2041
+ /* Hide horizontal gap cells and create vertical spacing with padding */
2042
+ .responsive-row[data-mobile-wrap="true"] .row-gap-td {
2043
+ display: none !important;
2044
+ width: 0 !important;
2045
+ height: 0 !important;
2046
+ }
2047
+
2048
+ /* Add vertical spacing between stacked cells using margin */
2049
+ .responsive-row[data-mobile-wrap="true"] .child-cell:not(:last-child) {
2050
+ margin-bottom: 20px !important;
2051
+ }
2052
+
2053
+ /* Dynamic gap support - common values */
2054
+ .responsive-row[data-mobile-wrap="true"][data-gap="10px"] .child-cell:not(:last-child) {
2055
+ margin-bottom: 10px !important;
2056
+ }
2057
+ .responsive-row[data-mobile-wrap="true"][data-gap="15px"] .child-cell:not(:last-child) {
2058
+ margin-bottom: 15px !important;
2059
+ }
2060
+ .responsive-row[data-mobile-wrap="true"][data-gap="20px"] .child-cell:not(:last-child) {
2061
+ margin-bottom: 20px !important;
2062
+ }
2063
+ .responsive-row[data-mobile-wrap="true"][data-gap="24px"] .child-cell:not(:last-child) {
2064
+ margin-bottom: 24px !important;
2065
+ }
2066
+ .responsive-row[data-mobile-wrap="true"][data-gap="30px"] .child-cell:not(:last-child) {
2067
+ margin-bottom: 30px !important;
2068
+ }
2069
+ .responsive-row[data-mobile-wrap="true"][data-gap="40px"] .child-cell:not(:last-child) {
2070
+ margin-bottom: 40px !important;
2071
+ }
1985
2072
  }
1986
2073
 
1987
2074
  /* ================================================= */
@@ -2061,8 +2148,10 @@ function Head({ children, backgroundColor = "#ffffff", title = "Email Preview",
2061
2148
  }
2062
2149
 
2063
2150
  function Heading({ config, devMode, children }) {
2064
- var _a;
2065
2151
  const { text, level = "h1", padding, color, textAlign, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, backgroundColor, } = config;
2152
+ // Determine the content to render
2153
+ const content = text !== null && text !== void 0 ? text : children;
2154
+ const isString = typeof content === "string";
2066
2155
  // 1. TD Style: Where padding, background, width, and verticalAlign are applied.
2067
2156
  const tdStyle = {
2068
2157
  padding: padding,
@@ -2089,7 +2178,6 @@ function Heading({ config, devMode, children }) {
2089
2178
  fontFamily: "Arial, Helvetica, sans-serif",
2090
2179
  // Outlook specific fixes (using string indexing)
2091
2180
  ["msoLineHeightRule"]: "exactly",
2092
- // ["mso-line-height-rule" as string]: "exactly",
2093
2181
  };
2094
2182
  // Dynamically create the Heading element
2095
2183
  const HeadingTag = level;
@@ -2098,7 +2186,7 @@ function Heading({ config, devMode, children }) {
2098
2186
  jsx("table", { "aria-label": "Heading Block Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2099
2187
  width: "100%",
2100
2188
  borderCollapse: "collapse",
2101
- }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: tdStyle, align: textAlign, children: jsx(HeadingTag, { style: headingStyle, dangerouslySetInnerHTML: { __html: (_a = text !== null && text !== void 0 ? text : children) !== null && _a !== void 0 ? _a : "" } }) }) }) }) }));
2189
+ }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: tdStyle, align: textAlign, children: isString ? (jsx(HeadingTag, { style: headingStyle, dangerouslySetInnerHTML: { __html: content } })) : (jsx(HeadingTag, { style: headingStyle, children: content })) }) }) }) }));
2102
2190
  }
2103
2191
  var Heading_default = memo(Heading, arePropsEqual);
2104
2192
 
@@ -2179,7 +2267,7 @@ function Image({ config, devNode, devMode }) {
2179
2267
  }
2180
2268
  var Image_default = memo(Image, arePropsEqual);
2181
2269
 
2182
- const justifyMap = {
2270
+ const justifyMap$1 = {
2183
2271
  start: "left",
2184
2272
  center: "center",
2185
2273
  end: "right",
@@ -2211,7 +2299,7 @@ function getBorderStyle$1(border) {
2211
2299
  return style;
2212
2300
  }
2213
2301
  function Row({ children, config, devNode }) {
2214
- var _a, _b, _c, _d, _e;
2302
+ var _a, _b, _c, _d, _e, _f;
2215
2303
  const childrenArray = (Array.isArray(children) ? children : [children]).filter((child) => child != null);
2216
2304
  const numChildren = childrenArray.length;
2217
2305
  // 1. Outer TD for Background and Border Radius (no border here)
@@ -2244,7 +2332,7 @@ function Row({ children, config, devNode }) {
2244
2332
  fontSize: "1px",
2245
2333
  };
2246
2334
  const tdAlign = config.justifyContent
2247
- ? justifyMap[config.justifyContent]
2335
+ ? justifyMap$1[config.justifyContent]
2248
2336
  : "left";
2249
2337
  const tdValign = config.alignItems ? alignMap[config.alignItems] : "top";
2250
2338
  return (jsxs("table", Object.assign({ "aria-label": "Row Outer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
@@ -2252,7 +2340,7 @@ function Row({ children, config, devNode }) {
2252
2340
  width: config.width || "100%",
2253
2341
  height: config.height,
2254
2342
  borderCollapse: "collapse",
2255
- } }, (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, className: "responsive-row", children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", Object.assign({ style: backgroundTdStyle }, (config.height && { height: config.height }), { children: jsx("table", { "aria-label": "Row Border Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: borderTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: paddingTdStyle, children: jsx("table", { "aria-label": "Row Justification Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2343
+ } }, (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: [jsx("tbody", { children: jsx("tr", { children: jsx("td", Object.assign({ style: backgroundTdStyle }, (config.height && { height: config.height }), { children: jsx("table", { "aria-label": "Row Border Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: borderTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: paddingTdStyle, children: jsx("table", { "aria-label": "Row Justification Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2256
2344
  width: "100%",
2257
2345
  height: "100%",
2258
2346
  borderCollapse: "collapse",
@@ -2262,7 +2350,7 @@ function Row({ children, config, devNode }) {
2262
2350
  padding: "0",
2263
2351
  margin: "0",
2264
2352
  }, className: "child-cell", children: child }), index < numChildren - 1 &&
2265
- config.gap && (jsx("td", { width: config.gap, style: gapTdStyle, children: "\u00A0" }, `row-gap-${index}`))] }, `row-child-${index}`))) }) }) })) }) }) }) }) }) }) }) }) })) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] })));
2353
+ 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 }) }) }))] })));
2266
2354
  }
2267
2355
  var Row_default = memo(Row, arePropsEqual);
2268
2356
 
@@ -2343,7 +2431,6 @@ function Spacer({ config, devNode }) {
2343
2431
  var Spacer_default = memo(Spacer, arePropsEqual);
2344
2432
 
2345
2433
  function Text({ config, devMode, children }) {
2346
- var _a;
2347
2434
  const { text, padding, color, textAlign, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, backgroundColor, opacity, whiteSpace, } = config;
2348
2435
  // 1. TD Style: Where padding and background are reliably applied.
2349
2436
  const tdStyle = {
@@ -2352,9 +2439,7 @@ function Text({ config, devMode, children }) {
2352
2439
  width: "100%",
2353
2440
  verticalAlign: "top",
2354
2441
  };
2355
- // 2. Content Style: Applied directly to a wrapper element (like a <div> or <p>)
2356
- // or inherited by the children. For max compatibility, we apply core styles
2357
- // directly to the TD or a wrapper <p> (if children is just a string).
2442
+ // 2. Content Style: Applied directly to a wrapper element
2358
2443
  const contentStyle = {
2359
2444
  color: color,
2360
2445
  textAlign: textAlign,
@@ -2369,18 +2454,187 @@ function Text({ config, devMode, children }) {
2369
2454
  verticalAlign: verticalAlign,
2370
2455
  opacity: opacity,
2371
2456
  whiteSpace: whiteSpace,
2372
- margin: "0", // Crucial: Remove default margin from <p> tags
2457
+ margin: "0",
2373
2458
  padding: "0",
2374
- fontFamily: "Arial, Helvetica, sans-serif", // Use a widely supported font stack
2459
+ fontFamily: "Arial, Helvetica, sans-serif",
2375
2460
  };
2376
- return (
2377
- // Wrap the text content in a table for padding/background/width management.
2378
- jsx("table", { "aria-label": "Text Block Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2461
+ // Determine content to render
2462
+ const content = text !== null && text !== void 0 ? text : children;
2463
+ const isString = typeof content === "string";
2464
+ return (jsx("table", { "aria-label": "Text Block Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2379
2465
  width: "100%",
2380
2466
  borderCollapse: "collapse",
2381
- }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: tdStyle, align: textAlign, children: jsx("div", { style: contentStyle, dangerouslySetInnerHTML: { __html: (_a = text !== null && text !== void 0 ? text : children) !== null && _a !== void 0 ? _a : "" } }) }) }) }) }));
2467
+ }, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: tdStyle, align: textAlign, children: isString ? (jsx("div", { style: contentStyle, dangerouslySetInnerHTML: { __html: content } })) : (jsx("div", { style: contentStyle, children: content })) }) }) }) }));
2382
2468
  }
2383
2469
  var Text_default = memo(Text, arePropsEqual);
2384
2470
 
2385
- export { Body, Button_default as Button, Column_default as Column, Container_default as Container, Divider_default as Divider, Head, Heading_default as Heading, Html, Image_default as Image, Row_default as Row, Section_default as Section, Spacer_default as Spacer, Text_default as Text };
2471
+ // Map alignment to HTML 'align' attribute
2472
+ const justifyMap = {
2473
+ start: "left",
2474
+ center: "center",
2475
+ end: "right",
2476
+ };
2477
+ // Helper to build Iconify API URL
2478
+ function buildIconifyUrl(config) {
2479
+ const { iconIdentifier, height = 24, color = "000000", rotate = 0, rotateOrientation = "cw", } = config;
2480
+ if (!iconIdentifier)
2481
+ return null;
2482
+ // Remove # from color if present
2483
+ const cleanColor = color.replace("#", "");
2484
+ // Build URL from template
2485
+ const template = process.env.ICONIFY_API_IMAGE_URI ||
2486
+ "https://iconify.pagenflow.com/api/image/{{height}}/{{color}}/{{rotate}}-{{rotate-orientation}}/{{icon-full-name}}.png";
2487
+ return template
2488
+ .replace("{{height}}", String(Number(height) * Number(2)))
2489
+ .replace("{{color}}", cleanColor)
2490
+ .replace("{{rotate}}", String(rotate))
2491
+ .replace("{{rotate-orientation}}", rotateOrientation)
2492
+ .replace("{{icon-full-name}}", iconIdentifier);
2493
+ }
2494
+ // Helper to build link href based on innerLink type
2495
+ function buildLinkHref(innerLink) {
2496
+ if (!innerLink || innerLink.type === "none")
2497
+ return null;
2498
+ switch (innerLink.type) {
2499
+ case "url":
2500
+ return innerLink.url || null;
2501
+ case "email":
2502
+ return innerLink.email ? `mailto:${innerLink.email}` : null;
2503
+ case "phone":
2504
+ return innerLink.phone ? `tel:${innerLink.phone}` : null;
2505
+ case "anchor":
2506
+ return innerLink.anchor ? `#${innerLink.anchor}` : null;
2507
+ case "page_top":
2508
+ return "#top";
2509
+ case "page_bottom":
2510
+ return "#bottom";
2511
+ default:
2512
+ return null;
2513
+ }
2514
+ }
2515
+ function Icon({ config, devNode, devMode, children }) {
2516
+ const {
2517
+ // base64Source,
2518
+ width, height, backgroundColor, padding = "0", borderRadius = "0", innerLink, justifyContent = "center", } = config;
2519
+ // Determine icon source
2520
+ const iconSrc = buildIconifyUrl(config);
2521
+ const href = buildLinkHref(innerLink);
2522
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_self";
2523
+ const align = justifyMap[justifyContent];
2524
+ // Convert width/height to string with px if number
2525
+ const widthStr = typeof width === "number" ? `${width}px` : width;
2526
+ const heightStr = typeof height === "number" ? `${height}px` : height;
2527
+ // Parse numeric values for HTML attributes
2528
+ const widthNum = typeof width === "number"
2529
+ ? width
2530
+ : typeof width === "string" && width.endsWith("px")
2531
+ ? parseInt(width, 10)
2532
+ : undefined;
2533
+ const heightNum = typeof height === "number"
2534
+ ? height
2535
+ : typeof height === "string" && height.endsWith("px")
2536
+ ? parseInt(height, 10)
2537
+ : undefined;
2538
+ // 1. Image Style: Critical for compatibility
2539
+ const imgStyle = {
2540
+ display: "block", // Prevents extra vertical space
2541
+ border: 0, // No default border
2542
+ width: widthStr || "auto",
2543
+ height: heightStr || "auto",
2544
+ };
2545
+ // 2. Link Style: No underline or color changes
2546
+ const linkStyle = {
2547
+ display: "block",
2548
+ textDecoration: "none",
2549
+ border: 0,
2550
+ outline: "none",
2551
+ };
2552
+ // 3. TD Style: Padding and background
2553
+ const tdStyle = {
2554
+ padding: padding,
2555
+ backgroundColor: backgroundColor,
2556
+ fontSize: "0", // CRITICAL: Collapses extra space
2557
+ lineHeight: "0", // CRITICAL: Collapses extra space7
2558
+ borderRadius: borderRadius,
2559
+ overflow: "hidden",
2560
+ };
2561
+ // --- VML Calculation for Outlook Compatibility ---
2562
+ const numericPadding = parseInt(padding.split(" ")[0] || "0", 10);
2563
+ const vmlWidth = (widthNum || 24) + numericPadding * 2;
2564
+ const vmlHeight = (heightNum || 24) + numericPadding * 2;
2565
+ // VML colors must use full hex format
2566
+ const vmlFillColor = (backgroundColor === null || backgroundColor === void 0 ? void 0 : backgroundColor.startsWith("#"))
2567
+ ? backgroundColor
2568
+ : backgroundColor
2569
+ ? `#${backgroundColor}`
2570
+ : "#ffffff";
2571
+ // Calculate arcsize percentage for VML
2572
+ const numericBorderRadius = parseInt(borderRadius || "0", 10);
2573
+ const arcsize = numericBorderRadius > 0
2574
+ ? Math.min((numericBorderRadius / Math.min(vmlWidth, vmlHeight)) * 100, 100)
2575
+ : 0;
2576
+ // Build VML code for Outlook
2577
+ const vmlIcon = backgroundColor && numericBorderRadius > 0
2578
+ ? `
2579
+ <!--[if mso]>
2580
+ <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}">
2581
+ <w:anchorlock/>
2582
+ <v:textbox inset="0,0,0,0" style="text-align: center;">
2583
+ <center style="padding:${padding};">
2584
+ <img src="${iconSrc || ""}" alt="" width="${widthNum || 24}" height="${heightNum || 24}" border="0" style="display:block;border:0;" />
2585
+ </center>
2586
+ </v:textbox>
2587
+ </v:roundrect>
2588
+ <![endif]-->
2589
+ `
2590
+ : "";
2591
+ // If no icon source, return empty
2592
+ if (!iconSrc && !devMode) {
2593
+ return null;
2594
+ }
2595
+ // Icon image element
2596
+ const iconElement = devMode && !!children ? (children) : iconSrc ? (jsx("img", { draggable: false, src: iconSrc, alt: "" // Icons are decorative, empty alt is appropriate
2597
+ , style: imgStyle, width: widthNum, height: heightNum, border: 0 })) : (jsx(Fragment$1, {}));
2598
+ // Wrap in link if href exists and not in dev mode
2599
+ const content = href && !devMode ? (jsx("a", Object.assign({ href: href, target: target, style: linkStyle }, (target === "_blank" ? { rel: "noopener noreferrer" } : {}), { children: iconElement }))) : (iconElement);
2600
+ // Build the HTML content with VML support (only when NOT in dev mode)
2601
+ const useVML = !devMode && backgroundColor && numericBorderRadius > 0;
2602
+ const htmlContent = useVML
2603
+ ? `
2604
+ ${vmlIcon}
2605
+ <!--[if !mso]><!-->
2606
+ <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse; width: 100%;">
2607
+ <tbody>
2608
+ <tr>
2609
+ <td style="background-color: ${backgroundColor}; border-radius: ${borderRadius}; padding: ${padding}; font-size: 0; line-height: 0; overflow: hidden;">
2610
+ ${href
2611
+ ? `<a href="${href}" target="${target}" style="display:block;text-decoration:none;border:0;outline:none;" ${target === "_blank" ? 'rel="noopener noreferrer"' : ""}>
2612
+ <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"};" />
2613
+ </a>`
2614
+ : `<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"};" />`}
2615
+ </td>
2616
+ </tr>
2617
+ </tbody>
2618
+ </table>
2619
+ <!--<![endif]-->
2620
+ `
2621
+ : null;
2622
+ return (jsxs("table", { "aria-label": "Icon", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: align, style: {
2623
+ // --- Start dev
2624
+ position: "relative",
2625
+ // --- End dev
2626
+ width: widthStr || "auto",
2627
+ borderCollapse: "collapse",
2628
+ // base
2629
+ boxSizing: "border-box",
2630
+ border: 0,
2631
+ margin: 0,
2632
+ padding: 0,
2633
+ }, onClick: devMode ? (e) => e.preventDefault() : undefined, children: [jsx("tbody", { children: jsx("tr", { children: useVML ? (jsx("td", { dangerouslySetInnerHTML: {
2634
+ __html: htmlContent !== null && htmlContent !== void 0 ? htmlContent : "",
2635
+ } })) : (jsx("td", { style: tdStyle, align: align, children: content })) }) }), devMode && !!devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
2636
+ }
2637
+ var Icon_default = memo(Icon, arePropsEqual);
2638
+
2639
+ export { Body, Button_default as Button, Column_default as Column, Container_default as Container, Divider_default as Divider, Head, Heading_default as Heading, Html, Icon_default as Icon, Image_default as Image, Row_default as Row, Section_default as Section, Spacer_default as Spacer, Text_default as Text };
2386
2640
  //# sourceMappingURL=index.esm.js.map