@pagenflow/email 1.4.6 → 1.4.8
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/components/Button.d.ts +3 -1
- package/dist/components/Column.d.ts +4 -1
- package/dist/components/Container.d.ts +4 -2
- package/dist/components/Divider.d.ts +3 -1
- package/dist/components/Heading.d.ts +6 -1
- package/dist/components/Icon.d.ts +4 -2
- package/dist/components/Image.d.ts +25 -2
- package/dist/components/Row.d.ts +20 -1
- package/dist/components/Section.d.ts +2 -0
- package/dist/components/Spacer.d.ts +6 -1
- package/dist/components/Text.d.ts +13 -1
- package/dist/components/utils/bindingAttribute.d.ts +38 -0
- package/dist/components/utils/linearizeStyle.d.ts +10 -0
- package/dist/index.cjs.js +368 -138
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +368 -138
- package/dist/index.esm.js.map +1 -1
- package/dist/types/DataBindings.d.ts +87 -0
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1531,6 +1531,57 @@ function arePropsEqual(prevProps, nextProps) {
|
|
|
1531
1531
|
return isEqual(prevProps, nextProps);
|
|
1532
1532
|
}
|
|
1533
1533
|
|
|
1534
|
+
/**
|
|
1535
|
+
* bindingAttribute.ts
|
|
1536
|
+
* -------------------
|
|
1537
|
+
* Serialises DataBindings into discrete HTML attributes:
|
|
1538
|
+
*
|
|
1539
|
+
* data-bind-if → visible condition → stamped on the root element
|
|
1540
|
+
* data-bind-list → repeater (dataList + itemAlias) → stamped on the
|
|
1541
|
+
* direct parent of the children loop, NOT the root
|
|
1542
|
+
* data-bind → propertyMap → stamped on the root element
|
|
1543
|
+
*
|
|
1544
|
+
* Keeping the three concerns in separate attributes lets the post-processor
|
|
1545
|
+
* handle them independently without ambiguity.
|
|
1546
|
+
*/
|
|
1547
|
+
// ─── Root element props (visible + propertyMap) ───────────────────────────────
|
|
1548
|
+
/**
|
|
1549
|
+
* Props to spread onto the component's root element.
|
|
1550
|
+
* Carries `data-bind-if` and/or `data-bind` (propertyMap).
|
|
1551
|
+
*/
|
|
1552
|
+
function rootBindingProps(bindings) {
|
|
1553
|
+
if (!bindings)
|
|
1554
|
+
return {};
|
|
1555
|
+
const props = {};
|
|
1556
|
+
if (bindings.visible) {
|
|
1557
|
+
props['data-bind-if'] = bindings.visible;
|
|
1558
|
+
}
|
|
1559
|
+
if (bindings.propertyMap && Object.keys(bindings.propertyMap).length) {
|
|
1560
|
+
props['data-bind'] = JSON.stringify({ propertyMap: bindings.propertyMap });
|
|
1561
|
+
}
|
|
1562
|
+
return props;
|
|
1563
|
+
}
|
|
1564
|
+
// ─── List wrapper props (dataList + itemAlias) ────────────────────────────────
|
|
1565
|
+
/**
|
|
1566
|
+
* Props to spread onto the *direct parent* of the children loop.
|
|
1567
|
+
* Carries `data-bind-list` only when a repeater is defined.
|
|
1568
|
+
*
|
|
1569
|
+
* Usage inside a component:
|
|
1570
|
+
* <tbody {...listBindingProps(bindings)}>
|
|
1571
|
+
* {children}
|
|
1572
|
+
* </tbody>
|
|
1573
|
+
*/
|
|
1574
|
+
function listBindingProps(bindings) {
|
|
1575
|
+
if (!(bindings === null || bindings === void 0 ? void 0 : bindings.dataList))
|
|
1576
|
+
return {};
|
|
1577
|
+
return {
|
|
1578
|
+
'data-bind-list': JSON.stringify({
|
|
1579
|
+
dataList: bindings.dataList,
|
|
1580
|
+
...(bindings.itemAlias && { itemAlias: bindings.itemAlias }),
|
|
1581
|
+
}),
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1534
1585
|
// Map alignment to HTML 'align' attribute
|
|
1535
1586
|
const justifyMap$3 = {
|
|
1536
1587
|
start: "left",
|
|
@@ -1538,7 +1589,7 @@ const justifyMap$3 = {
|
|
|
1538
1589
|
end: "right",
|
|
1539
1590
|
};
|
|
1540
1591
|
// Helper to build link href based on innerLink type (mirrors Icon component)
|
|
1541
|
-
function buildLinkHref$
|
|
1592
|
+
function buildLinkHref$4(innerLink) {
|
|
1542
1593
|
if (!innerLink || innerLink.type === "none")
|
|
1543
1594
|
return null;
|
|
1544
1595
|
switch (innerLink.type) {
|
|
@@ -1592,10 +1643,10 @@ function getBorderStyleString$2(border) {
|
|
|
1592
1643
|
}
|
|
1593
1644
|
return styles.join(" ");
|
|
1594
1645
|
}
|
|
1595
|
-
function Button({ config, devMode }) {
|
|
1646
|
+
function Button({ config, devMode, bindings }) {
|
|
1596
1647
|
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
1648
|
// Resolve href from innerLink
|
|
1598
|
-
const href = buildLinkHref$
|
|
1649
|
+
const href = buildLinkHref$4(innerLink);
|
|
1599
1650
|
const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
|
|
1600
1651
|
// Sanitize fontFamily early so safeFontFamily is available for all paths below.
|
|
1601
1652
|
const safeFontFamily = fontFamily
|
|
@@ -1679,7 +1730,7 @@ function Button({ config, devMode }) {
|
|
|
1679
1730
|
.join(" ");
|
|
1680
1731
|
return (
|
|
1681
1732
|
// Wrapper table for alignment - maintains proper positioning for hover indicators
|
|
1682
|
-
jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
|
|
1733
|
+
jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, ...rootBindingProps(bindings), style: {
|
|
1683
1734
|
width: "100%",
|
|
1684
1735
|
borderCollapse: "collapse",
|
|
1685
1736
|
boxSizing: "border-box",
|
|
@@ -1787,7 +1838,7 @@ function getBorderStyle$5(border) {
|
|
|
1787
1838
|
}
|
|
1788
1839
|
return style;
|
|
1789
1840
|
}
|
|
1790
|
-
function Column({ children, config, devNode }) {
|
|
1841
|
+
function Column({ children, config, devNode, bindings }) {
|
|
1791
1842
|
var _a, _b, _c;
|
|
1792
1843
|
// Process children array for gap support
|
|
1793
1844
|
const childrenArray = (Array.isArray(children) ? children : [children]).filter((child) => child != null);
|
|
@@ -1801,6 +1852,9 @@ function Column({ children, config, devNode }) {
|
|
|
1801
1852
|
};
|
|
1802
1853
|
// 2. Outer TD style: Background and Border Radius (no border here).
|
|
1803
1854
|
// height is set so the TD occupies the full declared height.
|
|
1855
|
+
// When maxWidth is set, the outer TD stays at its normal width so it
|
|
1856
|
+
// always fills its parent — the inner maxWidth table (see below) does
|
|
1857
|
+
// the actual capping.
|
|
1804
1858
|
const outerTdStyle = {
|
|
1805
1859
|
width: config.width,
|
|
1806
1860
|
height: config.height,
|
|
@@ -1836,6 +1890,7 @@ function Column({ children, config, devNode }) {
|
|
|
1836
1890
|
// treat it as content-box height and add padding on top, causing the
|
|
1837
1891
|
// total to exceed the declared height in preview mode.
|
|
1838
1892
|
verticalAlign: config.alignItems ? alignMap$2[config.alignItems] : "top",
|
|
1893
|
+
background: "transparent",
|
|
1839
1894
|
};
|
|
1840
1895
|
// 4. Gap spacer style (used between children)
|
|
1841
1896
|
const gapSpacerStyle = {
|
|
@@ -1843,12 +1898,25 @@ function Column({ children, config, devNode }) {
|
|
|
1843
1898
|
lineHeight: "1px",
|
|
1844
1899
|
fontSize: "1px",
|
|
1845
1900
|
width: "100%",
|
|
1901
|
+
background: "transparent",
|
|
1902
|
+
};
|
|
1903
|
+
// 5. maxWidth constraining table style (modern clients).
|
|
1904
|
+
// The `width` HTML attribute on this table is what Outlook Classic
|
|
1905
|
+
// (Word engine) reads — it has no concept of max-width, but it does
|
|
1906
|
+
// honour the `width` attribute as a hard column cap.
|
|
1907
|
+
// The CSS max-width here handles modern web/email clients correctly.
|
|
1908
|
+
// <center> around it ensures the constrained block stays horizontally
|
|
1909
|
+
// centred in both the Word engine and standards-based renderers.
|
|
1910
|
+
const maxWidthTableStyle = {
|
|
1911
|
+
width: "100%",
|
|
1912
|
+
maxWidth: config.maxWidth,
|
|
1913
|
+
borderCollapse: "collapse",
|
|
1846
1914
|
};
|
|
1847
1915
|
// Main content rendering
|
|
1848
|
-
const renderContent = () => (jsx("table", { "aria-label": "Column Padding", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: innerTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: innerTdStyle, valign: config.justifyContent ? vAlignMap[config.justifyContent] : "top", align: config.alignItems ? alignMap$2[config.alignItems] : "left",
|
|
1916
|
+
const renderContent = () => (jsx("table", { "aria-label": "Column Padding", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: innerTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: innerTdStyle, valign: config.justifyContent ? vAlignMap[config.justifyContent] : "top", align: config.alignItems ? alignMap$2[config.alignItems] : "left", ...(numChildren > 1 ? {} : listBindingProps(bindings)), children: numChildren > 1 ? (jsx("table", { "aria-label": "Column Gap Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
|
|
1849
1917
|
width: "100%",
|
|
1850
1918
|
borderCollapse: "collapse",
|
|
1851
|
-
}, children: jsx("tbody", { children: childrenArray.map((child, index) => (jsxs(Fragment, { children: [jsx("tr", { children: jsx("td", { style: {
|
|
1919
|
+
}, children: jsx("tbody", { ...listBindingProps(bindings), children: childrenArray.map((child, index) => (jsxs(Fragment, { children: [jsx("tr", { children: jsx("td", { style: {
|
|
1852
1920
|
verticalAlign: config.alignItems
|
|
1853
1921
|
? alignMap$2[config.alignItems]
|
|
1854
1922
|
: "top",
|
|
@@ -1857,10 +1925,27 @@ function Column({ children, config, devNode }) {
|
|
|
1857
1925
|
: "top", align: config.alignItems
|
|
1858
1926
|
? alignMap$2[config.alignItems]
|
|
1859
1927
|
: "left", children: child }) }), index < numChildren - 1 && (jsx("tr", { children: jsx("td", { style: gapSpacerStyle, children: "\u00A0" }) }))] }, `col-child-${index}`))) }) })) : (children) }) }) }) }));
|
|
1860
|
-
return (jsxs("table", { "aria-label": "Column Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
|
|
1928
|
+
return (jsxs("table", { "aria-label": "Column Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, ...rootBindingProps(bindings), style: {
|
|
1861
1929
|
position: "relative",
|
|
1862
1930
|
...outerTableStyle,
|
|
1863
|
-
}, ...(config.height && { height: config.height }), children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: outerTdStyle, ...(config.width && { width: config.width }), ...(config.height && { height: config.height }), children:
|
|
1931
|
+
}, ...(config.height && { height: config.height }), children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: outerTdStyle, ...(config.width && { width: config.width }), ...(config.height && { height: config.height }), children: config.maxWidth ? (
|
|
1932
|
+
/*
|
|
1933
|
+
* maxWidth wrapper — Outlook Classic compatibility pattern:
|
|
1934
|
+
*
|
|
1935
|
+
* <center> instructs the Word rendering engine to horizontally
|
|
1936
|
+
* centre its child block, equivalent to margin: 0 auto in CSS.
|
|
1937
|
+
*
|
|
1938
|
+
* The inner table carries the `width` HTML attribute set to the
|
|
1939
|
+
* maxWidth value. Outlook Classic reads `width` as a hard pixel
|
|
1940
|
+
* cap; it has no concept of max-width so this is the only lever
|
|
1941
|
+
* available. Modern clients receive the CSS max-width on the
|
|
1942
|
+
* same table and behave correctly.
|
|
1943
|
+
*
|
|
1944
|
+
* The outer column remains at its normal width so it always
|
|
1945
|
+
* fills its parent cell in every client — only the inner
|
|
1946
|
+
* content is capped.
|
|
1947
|
+
*/
|
|
1948
|
+
jsx("center", { children: jsx("table", { "aria-label": "Column Max Width Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, width: config.maxWidth, style: maxWidthTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { children: renderContent() }) }) }) }) })) : (renderContent()) }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
|
|
1864
1949
|
}
|
|
1865
1950
|
var Column_default = memo(Column, arePropsEqual);
|
|
1866
1951
|
|
|
@@ -1909,7 +1994,7 @@ function getBorderStyle$4(border) {
|
|
|
1909
1994
|
}
|
|
1910
1995
|
return style;
|
|
1911
1996
|
}
|
|
1912
|
-
function Container({ children, config, devMode, devNode }) {
|
|
1997
|
+
function Container({ children, config, bindings, devMode, devNode, }) {
|
|
1913
1998
|
var _a, _b, _c;
|
|
1914
1999
|
const { widthType, childrenConstraints } = config;
|
|
1915
2000
|
const childrenArray = (Array.isArray(children) ? children : [children]).filter((child) => child != null);
|
|
@@ -1927,7 +2012,7 @@ function Container({ children, config, devMode, devNode }) {
|
|
|
1927
2012
|
return 0;
|
|
1928
2013
|
})();
|
|
1929
2014
|
const getChildWidths = (() => {
|
|
1930
|
-
const { widthDistributionType } = childrenConstraints;
|
|
2015
|
+
const { widthDistributionType } = childrenConstraints !== null && childrenConstraints !== void 0 ? childrenConstraints : {};
|
|
1931
2016
|
const totalGapSpace = gapWidthPx * (numChildren > 1 ? numChildren - 1 : 0);
|
|
1932
2017
|
const remainingContentSpace = containerWidthPx - totalGapSpace;
|
|
1933
2018
|
switch (widthDistributionType) {
|
|
@@ -2001,6 +2086,7 @@ function Container({ children, config, devMode, devNode }) {
|
|
|
2001
2086
|
width: config.gap || "0",
|
|
2002
2087
|
lineHeight: "1px",
|
|
2003
2088
|
fontSize: "1px",
|
|
2089
|
+
background: "transparent",
|
|
2004
2090
|
};
|
|
2005
2091
|
const justifyAlign = config.justifyContent
|
|
2006
2092
|
? justifyMap$2[config.justifyContent]
|
|
@@ -2021,6 +2107,7 @@ function Container({ children, config, devMode, devNode }) {
|
|
|
2021
2107
|
fontSize: "0",
|
|
2022
2108
|
lineHeight: "0",
|
|
2023
2109
|
height: config.gap,
|
|
2110
|
+
background: "transparent",
|
|
2024
2111
|
}, children: "\u00A0" }))] }, `child-${index}`), jsx("td", { className: isStacking ? "desktop-gap-column" : undefined, width: config.gap, style: gapTdStyle, children: "\u00A0" }, `gap-${index}`)] }, `ctn:${index}`));
|
|
2025
2112
|
}
|
|
2026
2113
|
return (jsx("td", { className: isStacking ? "stack-td" : undefined, width: getChildWidths[index], style: childTdStyle, children: child }, `child-${index}`));
|
|
@@ -2028,7 +2115,7 @@ function Container({ children, config, devMode, devNode }) {
|
|
|
2028
2115
|
return (jsx("table", { "aria-label": `Container | Table Outer`, cellPadding: 0, cellSpacing: 0, role: "presentation", border: 0, style: {
|
|
2029
2116
|
position: "relative",
|
|
2030
2117
|
...outerTableStyle,
|
|
2031
|
-
}, children: jsx("tbody", { children: jsx("tr", { children: jsxs("td", { align: justifyAlign, children: [jsx("div", { dangerouslySetInnerHTML: { __html: msoFixedWrapper } }), jsxs("table", { className: [
|
|
2118
|
+
}, ...rootBindingProps(bindings), children: jsx("tbody", { children: jsx("tr", { children: jsxs("td", { align: justifyAlign, children: [jsx("div", { dangerouslySetInnerHTML: { __html: msoFixedWrapper } }), jsxs("table", { className: [
|
|
2032
2119
|
widthType === "fixed" ? "container-fixed-width" : undefined,
|
|
2033
2120
|
devMode ? "main-wrapper relative" : undefined,
|
|
2034
2121
|
]
|
|
@@ -2037,22 +2124,22 @@ function Container({ children, config, devMode, devNode }) {
|
|
|
2037
2124
|
width: "100%",
|
|
2038
2125
|
maxWidth: widthType === "fixed" ? config.width || "600px" : undefined,
|
|
2039
2126
|
borderCollapse: "collapse",
|
|
2040
|
-
}, width: containerWidthAttr, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: backgroundTdStyle, children: jsx("table", { "aria-label": `Container | Border Wrapper`, cellPadding: 0, cellSpacing: 0, role: "presentation", border: 0, style: borderTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: innerTdStyle, children: jsx("table", { "aria-label": `Container | Content Table`, cellPadding: 0, cellSpacing: 0, role: "presentation", border: 0, style: contentTableStyle, children: jsx("tbody", { children: jsx("tr", { children: rowElements }) }) }) }) }) }) }) }) }) }), !!devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }), jsx("div", { dangerouslySetInnerHTML: { __html: msoFixedFooter } })] }) }) }) }));
|
|
2127
|
+
}, width: containerWidthAttr, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: backgroundTdStyle, children: jsx("table", { "aria-label": `Container | Border Wrapper`, cellPadding: 0, cellSpacing: 0, role: "presentation", border: 0, style: borderTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: innerTdStyle, children: jsx("table", { "aria-label": `Container | Content Table`, cellPadding: 0, cellSpacing: 0, role: "presentation", border: 0, style: contentTableStyle, children: jsx("tbody", { children: jsx("tr", { ...listBindingProps(bindings), children: rowElements }) }) }) }) }) }) }) }) }) }), !!devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }), jsx("div", { dangerouslySetInnerHTML: { __html: msoFixedFooter } })] }) }) }) }));
|
|
2041
2128
|
}
|
|
2042
2129
|
var Container_default = memo(Container, arePropsEqual);
|
|
2043
2130
|
|
|
2044
|
-
function Divider({ config, devNode }) {
|
|
2131
|
+
function Divider({ config, devNode, bindings }) {
|
|
2045
2132
|
const { height = "1px", color = "#cccccc", width = "100%", margin = "20px 0", align = "center", hideOnMobile, } = config;
|
|
2046
2133
|
const heightPx = parseInt(height, 10) || 1;
|
|
2047
2134
|
// Parse margin into paddingTop / paddingBottom for the outer TD.
|
|
2048
2135
|
// Outlook ignores shorthand "20px 0" on TDs — must be explicit.
|
|
2049
|
-
const [marginTopRaw = "0", marginRightRaw = "0", marginBottomRaw, marginLeftRaw] = margin.trim().split(/\s+/);
|
|
2136
|
+
const [marginTopRaw = "0", marginRightRaw = "0", marginBottomRaw, marginLeftRaw,] = margin.trim().split(/\s+/);
|
|
2050
2137
|
const marginTop = marginTopRaw;
|
|
2051
2138
|
const marginBottom = marginBottomRaw !== null && marginBottomRaw !== void 0 ? marginBottomRaw : marginTopRaw; // "20px 0" → top=20px, bottom=20px
|
|
2052
2139
|
// Outlook requires align on the outer TD to correctly position
|
|
2053
2140
|
// a fixed-width inner table (e.g. width="300px").
|
|
2054
2141
|
const alignAttr = align === "left" ? "left" : align === "right" ? "right" : "center";
|
|
2055
|
-
return (jsxs("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
|
|
2142
|
+
return (jsxs("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, ...rootBindingProps(bindings), style: {
|
|
2056
2143
|
position: "relative", // dev overlay anchor
|
|
2057
2144
|
width: "100%",
|
|
2058
2145
|
borderCollapse: "collapse",
|
|
@@ -2192,36 +2279,7 @@ function Head({ children, backgroundColor = "#ffffff", title = "Email Preview",
|
|
|
2192
2279
|
mso-line-height-rule: exactly;
|
|
2193
2280
|
}
|
|
2194
2281
|
}
|
|
2195
|
-
|
|
2196
|
-
.row-content-table[data-mobile-justify="center"] { margin: 0 auto !important; float: none !important; }
|
|
2197
|
-
.row-content-table[data-mobile-justify="start"] { margin: 0 !important; float: left !important; }
|
|
2198
|
-
.row-content-table[data-mobile-justify="end"] { margin: 0 0 0 auto !important; float: right !important; }
|
|
2199
|
-
.row-content-table[data-mobile-align="center"] .child-cell { vertical-align: middle !important; }
|
|
2200
|
-
.row-content-table[data-mobile-align="start"] .child-cell { vertical-align: top !important; }
|
|
2201
|
-
.row-content-table[data-mobile-align="end"] .child-cell { vertical-align: bottom !important; }
|
|
2202
|
-
.row-content-table[data-mobile-wrap="true"] { width: 100% !important; max-width: 100% !important; }
|
|
2203
|
-
.row-content-table[data-mobile-wrap="true"] > tbody > .content-tr { display: block !important; }
|
|
2204
|
-
.row-content-table[data-mobile-wrap="true"] > tbody > .content-tr > .child-cell {
|
|
2205
|
-
display: block !important;
|
|
2206
|
-
width: 100% !important;
|
|
2207
|
-
box-sizing: border-box !important;
|
|
2208
|
-
}
|
|
2209
|
-
.row-content-table[data-mobile-wrap="true"] > tbody > .content-tr > .row-gap-td {
|
|
2210
|
-
display: none !important;
|
|
2211
|
-
width: 0 !important;
|
|
2212
|
-
height: 0 !important;
|
|
2213
|
-
}
|
|
2214
|
-
.row-content-table[data-mobile-wrap="true"] > tbody > .content-tr > .child-cell:not(:last-child) {
|
|
2215
|
-
margin-bottom: 20px !important;
|
|
2216
|
-
}
|
|
2217
|
-
${["10px", "15px", "20px", "24px", "30px", "40px", ...rowGaps]
|
|
2218
|
-
.filter((gap, index, self) => self.indexOf(gap) === index)
|
|
2219
|
-
.map((gap) => `
|
|
2220
|
-
.row-content-table[data-mobile-wrap="true"][data-gap="${gap}"] > tbody > .content-tr > .child-cell:not(:last-child) {
|
|
2221
|
-
margin-bottom: ${gap} !important;
|
|
2222
|
-
}`)
|
|
2223
|
-
.join("\n")}
|
|
2224
|
-
}
|
|
2282
|
+
|
|
2225
2283
|
a { color: inherit; text-decoration: none; }
|
|
2226
2284
|
ol, ul { margin: 0px; padding: 0px; list-style: none; }
|
|
2227
2285
|
li {
|
|
@@ -2373,7 +2431,9 @@ function resolveAnchorStyles(anchor, fallback) {
|
|
|
2373
2431
|
else if (fallback === null || fallback === void 0 ? void 0 : fallback[prop]) {
|
|
2374
2432
|
resolved[prop] = fallback[prop];
|
|
2375
2433
|
}
|
|
2376
|
-
|
|
2434
|
+
else if (prop === "text-decoration") {
|
|
2435
|
+
resolved[prop] = "none";
|
|
2436
|
+
}
|
|
2377
2437
|
}
|
|
2378
2438
|
return Object.entries(resolved)
|
|
2379
2439
|
.map(([k, v]) => `${k}:${v}`)
|
|
@@ -2398,8 +2458,32 @@ function injectLinkStyles(html, fallback) {
|
|
|
2398
2458
|
return result;
|
|
2399
2459
|
}
|
|
2400
2460
|
|
|
2401
|
-
|
|
2402
|
-
|
|
2461
|
+
// Helper to build link href based on innerLink type
|
|
2462
|
+
function buildLinkHref$3(innerLink) {
|
|
2463
|
+
if (!innerLink || innerLink.type === "none")
|
|
2464
|
+
return null;
|
|
2465
|
+
switch (innerLink.type) {
|
|
2466
|
+
case "url":
|
|
2467
|
+
return innerLink.url || null;
|
|
2468
|
+
case "email":
|
|
2469
|
+
return innerLink.email ? `mailto:${innerLink.email}` : null;
|
|
2470
|
+
case "phone":
|
|
2471
|
+
return innerLink.phone ? `tel:${innerLink.phone}` : null;
|
|
2472
|
+
case "anchor":
|
|
2473
|
+
return innerLink.anchor ? `#${innerLink.anchor}` : null;
|
|
2474
|
+
case "page_top":
|
|
2475
|
+
return "#top";
|
|
2476
|
+
case "page_bottom":
|
|
2477
|
+
return "#bottom";
|
|
2478
|
+
default:
|
|
2479
|
+
return null;
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
function Heading({ config, devMode, children, bindings }) {
|
|
2483
|
+
const { text, level = "h1", padding, color, textAlign, fontFamily, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, backgroundColor, wordBreak, whiteSpace, innerLink, } = config;
|
|
2484
|
+
// Resolve href and target from innerLink
|
|
2485
|
+
const href = buildLinkHref$3(innerLink);
|
|
2486
|
+
const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
|
|
2403
2487
|
// Determine the content to render
|
|
2404
2488
|
const content = text !== null && text !== void 0 ? text : children;
|
|
2405
2489
|
const isString = typeof content === "string";
|
|
@@ -2436,12 +2520,17 @@ function Heading({ config, devMode, children }) {
|
|
|
2436
2520
|
: "";
|
|
2437
2521
|
// Dynamically create the Heading element
|
|
2438
2522
|
const HeadingTag = level;
|
|
2523
|
+
const headingElement = isString ? (jsx(HeadingTag, { style: headingStyle, dangerouslySetInnerHTML: { __html: processedHtml } })) : (jsx(HeadingTag, { style: headingStyle, children: content }));
|
|
2439
2524
|
return (
|
|
2440
2525
|
// Wrap the heading content in a table for padding/width/background management.
|
|
2441
|
-
jsx("table", { "aria-label": "Heading Block Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
|
|
2526
|
+
jsx("table", { "aria-label": "Heading Block Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, ...rootBindingProps(bindings), style: {
|
|
2442
2527
|
width: "100%",
|
|
2443
2528
|
borderCollapse: "collapse",
|
|
2444
|
-
}, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: tdStyle, align: textAlign, children:
|
|
2529
|
+
}, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: tdStyle, align: textAlign, children: href && !devMode ? (jsx("a", { href: href, target: target, ...(target === "_blank" ? { rel: "noopener noreferrer" } : {}), style: {
|
|
2530
|
+
display: "block",
|
|
2531
|
+
textDecoration: "none",
|
|
2532
|
+
color: "inherit",
|
|
2533
|
+
}, children: headingElement })) : (headingElement) }) }) }) }));
|
|
2445
2534
|
}
|
|
2446
2535
|
var Heading_default = memo(Heading, arePropsEqual);
|
|
2447
2536
|
|
|
@@ -2466,7 +2555,7 @@ function Html({ children, backgroundColor = "#ffffff", }) {
|
|
|
2466
2555
|
}
|
|
2467
2556
|
|
|
2468
2557
|
// Helper to build link href based on innerLink type
|
|
2469
|
-
function buildLinkHref$
|
|
2558
|
+
function buildLinkHref$2(innerLink) {
|
|
2470
2559
|
if (!innerLink || innerLink.type === "none")
|
|
2471
2560
|
return null;
|
|
2472
2561
|
switch (innerLink.type) {
|
|
@@ -2526,11 +2615,15 @@ function getBorderStyleString$1(border) {
|
|
|
2526
2615
|
styles.push(`border-left:${border.left.width} ${border.left.style} ${border.left.color} !important;`);
|
|
2527
2616
|
return styles.join(" ");
|
|
2528
2617
|
}
|
|
2529
|
-
function Image({ config, devNode, devMode }) {
|
|
2618
|
+
function Image({ config, devNode, devMode, bindings }) {
|
|
2530
2619
|
var _a, _b;
|
|
2531
|
-
const { src, alt, innerLink, mobile } = config;
|
|
2620
|
+
const { src: originalSrc, alt, innerLink, mobile } = config;
|
|
2621
|
+
// In dev mode, if there's no src, use the placeholder
|
|
2622
|
+
const src = devMode && !originalSrc
|
|
2623
|
+
? "https://placehold.co/300x200?text=select+an+image&font=poppins"
|
|
2624
|
+
: originalSrc;
|
|
2532
2625
|
// Resolve href and target from innerLink
|
|
2533
|
-
const href = buildLinkHref$
|
|
2626
|
+
const href = buildLinkHref$2(innerLink);
|
|
2534
2627
|
const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
|
|
2535
2628
|
const seed = src + (alt || "");
|
|
2536
2629
|
const instanceId = seed
|
|
@@ -2544,43 +2637,37 @@ function Image({ config, devNode, devMode }) {
|
|
|
2544
2637
|
const widthAttr = desktopWidth.replace("px", "");
|
|
2545
2638
|
const heightAttr = (_a = config.height) === null || _a === void 0 ? void 0 : _a.replace("px", "");
|
|
2546
2639
|
// Determine the table's "initial" width.
|
|
2547
|
-
// If it's 300px, the table should be 300px, not 100%.
|
|
2548
2640
|
const tableWidth = isPercent ? desktopWidth : `${widthAttr}px`;
|
|
2549
|
-
//
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2641
|
+
// Calculate width attribute for Outlook
|
|
2642
|
+
let imgWidthAttr;
|
|
2643
|
+
if (config.outlookWidth) {
|
|
2644
|
+
// Use explicit outlookWidth if provided
|
|
2645
|
+
imgWidthAttr = parseInt(config.outlookWidth, 10);
|
|
2646
|
+
}
|
|
2647
|
+
else if (isPercent) {
|
|
2648
|
+
// For percentage widths, use maxWidth as fallback for Outlook
|
|
2649
|
+
const maxWidthPx = ((_b = config.maxWidth) === null || _b === void 0 ? void 0 : _b.endsWith("px"))
|
|
2650
|
+
? parseInt(config.maxWidth, 10)
|
|
2651
|
+
: undefined;
|
|
2652
|
+
imgWidthAttr = maxWidthPx !== null && maxWidthPx !== void 0 ? maxWidthPx : undefined;
|
|
2653
|
+
}
|
|
2654
|
+
else {
|
|
2655
|
+
// For fixed pixel widths, use that value
|
|
2656
|
+
imgWidthAttr = widthAttr ? parseInt(widthAttr, 10) : undefined;
|
|
2657
|
+
}
|
|
2658
|
+
// 2. Mobile Overrides
|
|
2566
2659
|
let mobileCss = "";
|
|
2567
2660
|
if (mobile) {
|
|
2568
|
-
|
|
2569
|
-
const wrapRules = [
|
|
2570
|
-
// Always reset min-width so the px lock from desktop can be overridden
|
|
2571
|
-
"min-width: 0 !important;",
|
|
2572
|
-
];
|
|
2661
|
+
const wrapRules = ["min-width: 0 !important;"];
|
|
2573
2662
|
if (mobile.width !== undefined)
|
|
2574
2663
|
wrapRules.push(`width: ${mobile.width} !important;`);
|
|
2575
2664
|
if (mobile.maxWidth !== undefined)
|
|
2576
2665
|
wrapRules.push(`max-width: ${mobile.maxWidth} !important;`);
|
|
2577
|
-
// .td-${imgClass} rules
|
|
2578
2666
|
const tdRules = [];
|
|
2579
2667
|
if (mobile.padding !== undefined)
|
|
2580
2668
|
tdRules.push(`padding: ${mobile.padding} !important;`);
|
|
2581
2669
|
if (mobile.backgroundColor !== undefined)
|
|
2582
2670
|
tdRules.push(`background-color: ${mobile.backgroundColor} !important;`);
|
|
2583
|
-
// .${imgClass} rules
|
|
2584
2671
|
const imgRules = [];
|
|
2585
2672
|
if (mobile.width !== undefined)
|
|
2586
2673
|
imgRules.push(`width: ${mobile.width} !important;`);
|
|
@@ -2603,7 +2690,6 @@ function Image({ config, devNode, devMode }) {
|
|
|
2603
2690
|
mobileCss = `
|
|
2604
2691
|
@media screen and (max-width: 768px) {
|
|
2605
2692
|
.wrap-${imgClass} {
|
|
2606
|
-
/* This breaks the px lock from desktop and makes it fluid */
|
|
2607
2693
|
${wrapRules.join("\n ")}
|
|
2608
2694
|
}
|
|
2609
2695
|
.td-${imgClass} {
|
|
@@ -2629,9 +2715,27 @@ function Image({ config, devNode, devMode }) {
|
|
|
2629
2715
|
objectPosition: config.objectPosition,
|
|
2630
2716
|
};
|
|
2631
2717
|
const imageElement = (jsx("img", { src: src, alt: alt, width: imgWidthAttr, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle, draggable: !devMode }));
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2718
|
+
// Outlook Classic wrapper - only applied when outlookWidth is explicitly set
|
|
2719
|
+
// OR when we need to constrain a percentage-width image in Outlook
|
|
2720
|
+
const needsOutlookWrapper = config.outlookWidth || (isPercent && imgWidthAttr);
|
|
2721
|
+
const finalImageElement = needsOutlookWrapper ? (jsxs(Fragment$1, { children: [jsx("style", { dangerouslySetInnerHTML: {
|
|
2722
|
+
__html: `</style>` +
|
|
2723
|
+
`<!--[if mso]>` +
|
|
2724
|
+
`<table cellpadding="0" cellspacing="0" border="0" style="width: ${imgWidthAttr}px;">` +
|
|
2725
|
+
`<tr>` +
|
|
2726
|
+
`<td style="padding: 0; margin: 0;" width="${imgWidthAttr}">` +
|
|
2727
|
+
`<img src="${src}" alt="${alt || ""}" width="${imgWidthAttr}" height="${heightAttr !== "auto" ? heightAttr : ""}" style="display: block; width: 100%; height: auto;" />` +
|
|
2728
|
+
`</td>` +
|
|
2729
|
+
`</tr>` +
|
|
2730
|
+
`</table>` +
|
|
2731
|
+
`<![endif]-->` +
|
|
2732
|
+
`<!--[if !mso]><!-->` +
|
|
2733
|
+
`<style>`,
|
|
2734
|
+
} }), imageElement, jsx("style", { dangerouslySetInnerHTML: {
|
|
2735
|
+
__html: `</style>` + `<!--<![endif]-->` + `<style>`,
|
|
2736
|
+
} })] })) : (imageElement);
|
|
2737
|
+
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", ...rootBindingProps(bindings), style: {
|
|
2738
|
+
width: tableWidth,
|
|
2635
2739
|
maxWidth: "100%",
|
|
2636
2740
|
borderCollapse: "collapse",
|
|
2637
2741
|
margin: "0 auto",
|
|
@@ -2640,10 +2744,10 @@ function Image({ config, devNode, devMode }) {
|
|
|
2640
2744
|
backgroundColor: config.backgroundColor,
|
|
2641
2745
|
fontSize: "0",
|
|
2642
2746
|
lineHeight: "0",
|
|
2643
|
-
width: tableWidth,
|
|
2747
|
+
width: tableWidth,
|
|
2644
2748
|
}, children: href && !devMode ? (jsx("a", { href: href, target: target, ...(target === "_blank"
|
|
2645
2749
|
? { rel: "noopener noreferrer" }
|
|
2646
|
-
: {}), style: { display: "block", width: "100%" }, children:
|
|
2750
|
+
: {}), style: { display: "block", width: "100%" }, children: finalImageElement })) : (finalImageElement) }) }) }), devMode && !!devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] })] }));
|
|
2647
2751
|
}
|
|
2648
2752
|
var Image_default = memo(Image, arePropsEqual);
|
|
2649
2753
|
|
|
@@ -2712,12 +2816,34 @@ function getHrefFromInnerLink(innerLink) {
|
|
|
2712
2816
|
return undefined;
|
|
2713
2817
|
}
|
|
2714
2818
|
}
|
|
2715
|
-
|
|
2716
|
-
|
|
2819
|
+
/**
|
|
2820
|
+
* Resolves the width for a child <td> at the given index based on layoutColumns.
|
|
2821
|
+
*
|
|
2822
|
+
* - "equal" → equal percentage share across all children
|
|
2823
|
+
* - string[] → explicit value at the matching index (px, %, or mixed)
|
|
2824
|
+
* - undefined → undefined, so no width attribute is set (retrocompat)
|
|
2825
|
+
*/
|
|
2826
|
+
function resolveChildColumnWidth(layoutColumns, index, numChildren) {
|
|
2827
|
+
if (!layoutColumns)
|
|
2828
|
+
return undefined;
|
|
2829
|
+
if (layoutColumns === "equal")
|
|
2830
|
+
return `${100 / numChildren}%`;
|
|
2831
|
+
return layoutColumns[index];
|
|
2832
|
+
}
|
|
2833
|
+
function Row({ children, config, devNode, devMode, bindings }) {
|
|
2834
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2717
2835
|
const childrenArray = (Array.isArray(children) ? children : [children]).filter((child) => child != null);
|
|
2718
2836
|
const numChildren = childrenArray.length;
|
|
2719
2837
|
const href = getHrefFromInnerLink(config.innerLink);
|
|
2720
2838
|
const target = ((_a = config.innerLink) === null || _a === void 0 ? void 0 : _a.target) || "_blank";
|
|
2839
|
+
// Whether children should stack on mobile.
|
|
2840
|
+
// Mirrors Container's isStacking pattern: drives stack-td / desktop-gap-column
|
|
2841
|
+
// / mobile-gap-spacer class names so that stacking works via non-@media CSS
|
|
2842
|
+
// rules that survive Gmail's stylesheet stripping.
|
|
2843
|
+
const isStacking = ((_b = config.mobile) === null || _b === void 0 ? void 0 : _b.wrap) === true && numChildren > 1;
|
|
2844
|
+
// Whether layoutColumns is active. When true, table-layout:fixed is applied
|
|
2845
|
+
// to the content table so Outlook Classic honours the declared column widths.
|
|
2846
|
+
const hasLayoutColumns = config.layoutColumns !== undefined;
|
|
2721
2847
|
// 1. Outer TD: Background, Border Radius, Width, Height.
|
|
2722
2848
|
const backgroundTdStyle = {
|
|
2723
2849
|
backgroundColor: config.backgroundColor,
|
|
@@ -2727,9 +2853,9 @@ function Row({ children, config, devNode, devMode }) {
|
|
|
2727
2853
|
backgroundImage: config.backgroundImage
|
|
2728
2854
|
? `url(${config.backgroundImage.src})`
|
|
2729
2855
|
: undefined,
|
|
2730
|
-
backgroundRepeat: (
|
|
2731
|
-
backgroundSize: (
|
|
2732
|
-
backgroundPosition: (
|
|
2856
|
+
backgroundRepeat: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.repeat,
|
|
2857
|
+
backgroundSize: (_d = config.backgroundImage) === null || _d === void 0 ? void 0 : _d.size,
|
|
2858
|
+
backgroundPosition: (_e = config.backgroundImage) === null || _e === void 0 ? void 0 : _e.position,
|
|
2733
2859
|
...(config.borderRadius && { overflow: "hidden" }),
|
|
2734
2860
|
};
|
|
2735
2861
|
// 2. Inner Table: Border and Border Radius.
|
|
@@ -2758,24 +2884,34 @@ function Row({ children, config, devNode, devMode }) {
|
|
|
2758
2884
|
// Content table fills available space, giving Outlook Classic a hard
|
|
2759
2885
|
// boundary so text children get a constrained box and line wrapping
|
|
2760
2886
|
// triggers correctly. Use for rows containing text + image layouts.
|
|
2887
|
+
//
|
|
2888
|
+
// layoutColumns (any value) → additionally applies table-layout: fixed
|
|
2889
|
+
// so Outlook Classic honours the per-child width declarations.
|
|
2890
|
+
// Compatible with both fillWidth modes.
|
|
2761
2891
|
const contentTableStyle = {
|
|
2762
|
-
|
|
2892
|
+
// When layoutColumns is active, force 100% so Outlook Classic has a
|
|
2893
|
+
// concrete boundary to resolve percentage column widths against.
|
|
2894
|
+
// table-layout:fixed is meaningless without a fixed reference width.
|
|
2895
|
+
width: hasLayoutColumns || config.fillWidth ? "100%" : "auto",
|
|
2763
2896
|
height: "100%",
|
|
2764
2897
|
borderCollapse: "collapse",
|
|
2765
2898
|
minWidth: "1px",
|
|
2766
|
-
...(!config.fillWidth &&
|
|
2899
|
+
...(!config.fillWidth &&
|
|
2900
|
+
!hasLayoutColumns && { maxWidth: config.width || "100%" }),
|
|
2901
|
+
...(hasLayoutColumns && { tableLayout: "fixed" }),
|
|
2767
2902
|
};
|
|
2768
2903
|
// 5. Gap TD.
|
|
2769
2904
|
const gapTdStyle = {
|
|
2770
2905
|
width: config.gap || "0",
|
|
2771
2906
|
lineHeight: "1px",
|
|
2772
2907
|
fontSize: "1px",
|
|
2908
|
+
background: "transparent",
|
|
2773
2909
|
};
|
|
2774
2910
|
const tdAlign = config.justifyContent
|
|
2775
2911
|
? justifyMap$1[config.justifyContent]
|
|
2776
2912
|
: "left";
|
|
2777
2913
|
const tdValign = config.alignItems ? alignMap[config.alignItems] : "top";
|
|
2778
|
-
const content = (jsxs("table", { "aria-label": "Row Outer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
|
|
2914
|
+
const content = (jsxs("table", { "aria-label": "Row Outer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, ...(!href ? rootBindingProps(bindings) : {}), style: {
|
|
2779
2915
|
position: "relative",
|
|
2780
2916
|
width: config.width || "100%",
|
|
2781
2917
|
height: config.height,
|
|
@@ -2784,15 +2920,44 @@ function Row({ children, config, devNode, devMode }) {
|
|
|
2784
2920
|
width: "100%",
|
|
2785
2921
|
height: "100%",
|
|
2786
2922
|
borderCollapse: "collapse",
|
|
2787
|
-
}, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { align: tdAlign, width: "100%", style: { width: "100%" }, children: jsx("table", { "aria-label": "Row Content", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: contentTableStyle, ...(config.height && { height: config.height }), className: "content-table row-content-table", "data-mobile-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2923
|
+
}, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { align: tdAlign, width: "100%", style: { width: "100%" }, children: jsx("table", { "aria-label": "Row Content", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: contentTableStyle, ...(config.height && { height: config.height }), className: "content-table row-content-table", "data-mobile-wrap": ((_f = config.mobile) === null || _f === void 0 ? void 0 : _f.wrap) ? "true" : undefined, "data-gap": config.gap, children: jsx("tbody", { children: jsx("tr", { className: "content-tr", ...listBindingProps(bindings), children: childrenArray.map((child, index) => {
|
|
2924
|
+
// Resolve the column width for this child based on
|
|
2925
|
+
// layoutColumns. undefined when layoutColumns is not set,
|
|
2926
|
+
// preserving the original behaviour (retrocompat).
|
|
2927
|
+
const columnWidth = resolveChildColumnWidth(config.layoutColumns, index, numChildren);
|
|
2928
|
+
return (jsxs(Fragment, { children: [jsxs("td", { align: tdAlign, ...(columnWidth && {
|
|
2929
|
+
width: columnWidth,
|
|
2930
|
+
}), style: {
|
|
2931
|
+
verticalAlign: tdValign,
|
|
2932
|
+
textAlign: tdAlign,
|
|
2933
|
+
padding: "0",
|
|
2934
|
+
margin: "0",
|
|
2935
|
+
...(columnWidth && {
|
|
2936
|
+
width: columnWidth,
|
|
2937
|
+
}),
|
|
2938
|
+
},
|
|
2939
|
+
// Mirror of Container's stack-td pattern: when isStacking,
|
|
2940
|
+
// the non-@media .stack-td rule forces display:block +
|
|
2941
|
+
// width:100% on each child, which survives Gmail's
|
|
2942
|
+
// @media stripping and achieves true mobile stacking.
|
|
2943
|
+
className: `child-cell${isStacking ? " stack-td" : ""}`, children: [child, isStacking &&
|
|
2944
|
+
index < numChildren - 1 &&
|
|
2945
|
+
config.gap && (jsx("div", { className: "mobile-gap-spacer", style: {
|
|
2946
|
+
display: "none",
|
|
2947
|
+
fontSize: "0",
|
|
2948
|
+
lineHeight: "0",
|
|
2949
|
+
height: config.gap,
|
|
2950
|
+
background: "transparent",
|
|
2951
|
+
}, children: "\u00A0" }))] }), index < numChildren - 1 &&
|
|
2952
|
+
config.gap && (jsx("td", { width: config.gap, style: gapTdStyle,
|
|
2953
|
+
// Mirror of Container's desktop-gap-column pattern:
|
|
2954
|
+
// when isStacking, the non-@media .desktop-gap-column
|
|
2955
|
+
// rule collapses the between-column gap td so it does
|
|
2956
|
+
// not create phantom space while children are stacked.
|
|
2957
|
+
className: `row-gap-td${isStacking ? " desktop-gap-column" : ""}`, children: "\u00A0" }, `row-gap-${index}`))] }, `row-child-${index}`));
|
|
2958
|
+
}) }) }) }) }) }) }) }) }) }) }) }) }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
|
|
2794
2959
|
if (href && !devMode) {
|
|
2795
|
-
return (jsx("a", { href: href, ...({ target }), style: {
|
|
2960
|
+
return (jsx("a", { href: href, ...({ target }), ...rootBindingProps(bindings), style: {
|
|
2796
2961
|
textDecoration: "none",
|
|
2797
2962
|
color: "inherit",
|
|
2798
2963
|
display: "block",
|
|
@@ -2837,10 +3002,10 @@ function getBorderStyle$1(border) {
|
|
|
2837
3002
|
}
|
|
2838
3003
|
return style;
|
|
2839
3004
|
}
|
|
2840
|
-
const Section = ({ config, children, devNode, }) => {
|
|
3005
|
+
const Section = ({ config, children, devNode, bindings, }) => {
|
|
2841
3006
|
var _a, _b, _c;
|
|
2842
3007
|
const { sectionType, padding } = config;
|
|
2843
|
-
return (jsxs("table", { "aria-label": `Section |Table | ${sectionType}`, role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
|
|
3008
|
+
return (jsxs("table", { "aria-label": `Section |Table | ${sectionType}`, role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, ...rootBindingProps(bindings), style: {
|
|
2844
3009
|
position: "relative",
|
|
2845
3010
|
width: "100%",
|
|
2846
3011
|
backgroundColor: config.backgroundColor,
|
|
@@ -2853,66 +3018,98 @@ const Section = ({ config, children, devNode, }) => {
|
|
|
2853
3018
|
backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position,
|
|
2854
3019
|
}, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: {
|
|
2855
3020
|
padding: padding,
|
|
2856
|
-
}, children: children }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children:
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
3021
|
+
}, ...listBindingProps(bindings), children: children }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: jsxs("span", { style: {
|
|
3022
|
+
backgroundColor: "black",
|
|
3023
|
+
color: "white",
|
|
3024
|
+
padding: "4px",
|
|
3025
|
+
fontSize: "14px",
|
|
3026
|
+
position: "absolute",
|
|
3027
|
+
left: 0,
|
|
3028
|
+
top: 0,
|
|
3029
|
+
}, children: ["Section | ", sectionType] }) }) }) }))] }));
|
|
2865
3030
|
};
|
|
2866
3031
|
var Section_default = memo(Section, arePropsEqual);
|
|
2867
3032
|
|
|
2868
|
-
function Spacer({ config, devNode }) {
|
|
2869
|
-
|
|
3033
|
+
function Spacer({ config, devNode, bindings }) {
|
|
3034
|
+
var _a, _b, _c;
|
|
3035
|
+
const { height, hideOnMobile, backgroundColor, backgroundImage } = config;
|
|
2870
3036
|
// 1. Spacer Table Style
|
|
2871
3037
|
const spacerTableStyle = {
|
|
2872
|
-
// Crucial for compatibility: Ensures no background or border interference
|
|
2873
|
-
backgroundColor: "transparent",
|
|
2874
3038
|
borderCollapse: "collapse",
|
|
2875
|
-
border: "0",
|
|
2876
3039
|
width: "100%",
|
|
2877
|
-
// Note the CSS standard dash convention: 'mso-table-lspace'
|
|
2878
|
-
// ["mso-table-lspace" as string]: "0pt",
|
|
2879
|
-
["msoTableLspace"]: "0pt",
|
|
2880
|
-
// ["mso-table-rspace" as string]: "0pt",
|
|
2881
|
-
["msoTableRspace"]: "0pt",
|
|
2882
3040
|
};
|
|
2883
3041
|
// 2. Spacer TD Style: The element that creates the actual vertical space
|
|
2884
3042
|
const spacerTdStyle = {
|
|
2885
3043
|
height: height,
|
|
2886
|
-
|
|
3044
|
+
maxHeight: height,
|
|
2887
3045
|
fontSize: "0",
|
|
2888
3046
|
lineHeight: "0",
|
|
2889
3047
|
padding: "0",
|
|
3048
|
+
margin: "0",
|
|
3049
|
+
border: "none",
|
|
3050
|
+
color: "transparent",
|
|
3051
|
+
// Background applied at TD level for Outlook Classic compatibility
|
|
3052
|
+
...(backgroundColor && { backgroundColor }),
|
|
3053
|
+
...(backgroundImage && {
|
|
3054
|
+
backgroundImage: `url(${backgroundImage.src})`,
|
|
3055
|
+
backgroundRepeat: (_a = backgroundImage.repeat) !== null && _a !== void 0 ? _a : "no-repeat",
|
|
3056
|
+
backgroundSize: (_b = backgroundImage.size) !== null && _b !== void 0 ? _b : "cover",
|
|
3057
|
+
backgroundPosition: (_c = backgroundImage.position) !== null && _c !== void 0 ? _c : "center",
|
|
3058
|
+
}),
|
|
2890
3059
|
};
|
|
2891
3060
|
// Parse height for the HTML attribute
|
|
2892
3061
|
const spacerHeightAttribute = parseInt(height, 10) || 1;
|
|
2893
|
-
return (
|
|
2894
|
-
// Outer table ensures the spacer spans the full width of its container
|
|
2895
|
-
jsxs("table", { "aria-label": "Vertical Spacer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
|
|
3062
|
+
return (jsxs("table", { "aria-label": "Vertical Spacer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, ...rootBindingProps(bindings), style: {
|
|
2896
3063
|
// --- Start dev
|
|
2897
3064
|
position: "relative",
|
|
2898
3065
|
// --- End dev
|
|
2899
3066
|
...spacerTableStyle,
|
|
2900
|
-
}, ...{ height: spacerHeightAttribute }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: spacerTdStyle,
|
|
2901
|
-
|
|
2902
|
-
|
|
3067
|
+
}, ...{ height: spacerHeightAttribute }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: spacerTdStyle, height: spacerHeightAttribute, ...(backgroundColor && !backgroundImage
|
|
3068
|
+
? { bgcolor: backgroundColor }
|
|
3069
|
+
: {}) }) }) }), devNode && (jsx("tfoot", { children: jsx("tr", { children: jsx("td", { children: devNode }) }) }))] }));
|
|
2903
3070
|
}
|
|
2904
3071
|
var Spacer_default = memo(Spacer, arePropsEqual);
|
|
2905
3072
|
|
|
2906
|
-
|
|
2907
|
-
|
|
3073
|
+
// Helper to build link href based on innerLink type
|
|
3074
|
+
function buildLinkHref$1(innerLink) {
|
|
3075
|
+
if (!innerLink || innerLink.type === "none")
|
|
3076
|
+
return null;
|
|
3077
|
+
switch (innerLink.type) {
|
|
3078
|
+
case "url":
|
|
3079
|
+
return innerLink.url || null;
|
|
3080
|
+
case "email":
|
|
3081
|
+
return innerLink.email ? `mailto:${innerLink.email}` : null;
|
|
3082
|
+
case "phone":
|
|
3083
|
+
return innerLink.phone ? `tel:${innerLink.phone}` : null;
|
|
3084
|
+
case "anchor":
|
|
3085
|
+
return innerLink.anchor ? `#${innerLink.anchor}` : null;
|
|
3086
|
+
case "page_top":
|
|
3087
|
+
return "#top";
|
|
3088
|
+
case "page_bottom":
|
|
3089
|
+
return "#bottom";
|
|
3090
|
+
default:
|
|
3091
|
+
return null;
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
function Text({ config, devMode, children, bindings }) {
|
|
3095
|
+
const { text, padding, color, textAlign, fontFamily, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, backgroundColor, opacity, whiteSpace, wordBreak = "break-all", maxWidth, innerLink, } = config;
|
|
3096
|
+
// Resolve href and target from innerLink
|
|
3097
|
+
const href = buildLinkHref$1(innerLink);
|
|
3098
|
+
const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
|
|
2908
3099
|
// 1. TD Style: Where padding and background are reliably applied.
|
|
3100
|
+
// When maxWidth is set, this TD stays at width: 100% so it always fills
|
|
3101
|
+
// its parent — the inner maxWidth table (see below) does the actual
|
|
3102
|
+
// capping, keeping the outer layout intact in all clients.
|
|
2909
3103
|
const tdStyle = {
|
|
2910
3104
|
padding: padding,
|
|
2911
3105
|
backgroundColor: backgroundColor,
|
|
2912
3106
|
width: "100%",
|
|
2913
3107
|
verticalAlign: "top",
|
|
2914
3108
|
};
|
|
2915
|
-
// 2. Content Style: Applied directly to
|
|
3109
|
+
// 2. Content Style: Applied directly to the inner div wrapper.
|
|
3110
|
+
// maxWidth is intentionally omitted here — putting it on a div has no
|
|
3111
|
+
// effect in Outlook Classic (Word engine ignores CSS on div elements).
|
|
3112
|
+
// The constraint is enforced at the table level instead (see below).
|
|
2916
3113
|
const contentStyle = {
|
|
2917
3114
|
color: color,
|
|
2918
3115
|
textAlign: textAlign,
|
|
@@ -2931,7 +3128,18 @@ function Text({ config, devMode, children }) {
|
|
|
2931
3128
|
wordBreak: wordBreak,
|
|
2932
3129
|
margin: "0",
|
|
2933
3130
|
padding: "0",
|
|
2934
|
-
|
|
3131
|
+
};
|
|
3132
|
+
// 3. maxWidth constraining table style (modern clients).
|
|
3133
|
+
// The `width` HTML attribute on this table is what Outlook Classic
|
|
3134
|
+
// (Word engine) reads — it has no concept of max-width, but it does
|
|
3135
|
+
// honour the `width` attribute as a hard column cap.
|
|
3136
|
+
// The CSS max-width here handles modern web/email clients correctly.
|
|
3137
|
+
// <center> around it ensures the constrained block stays horizontally
|
|
3138
|
+
// centred in both the Word engine and standards-based renderers.
|
|
3139
|
+
const maxWidthTableStyle = {
|
|
3140
|
+
width: "100%",
|
|
3141
|
+
maxWidth: maxWidth,
|
|
3142
|
+
borderCollapse: "collapse",
|
|
2935
3143
|
};
|
|
2936
3144
|
// Determine content to render
|
|
2937
3145
|
const content = text !== null && text !== void 0 ? text : children;
|
|
@@ -2939,10 +3147,32 @@ function Text({ config, devMode, children }) {
|
|
|
2939
3147
|
const processedHtml = isString
|
|
2940
3148
|
? injectLinkStyles(content, contentStyle)
|
|
2941
3149
|
: "";
|
|
2942
|
-
|
|
3150
|
+
const innerContent = isString ? (jsx("div", { style: contentStyle, dangerouslySetInnerHTML: { __html: processedHtml } })) : (jsx("div", { style: contentStyle, children: content }));
|
|
3151
|
+
const wrappedContent = maxWidth ? (
|
|
3152
|
+
/*
|
|
3153
|
+
* maxWidth wrapper — Outlook Classic compatibility pattern:
|
|
3154
|
+
*
|
|
3155
|
+
* <center> instructs the Word rendering engine to horizontally
|
|
3156
|
+
* centre its child block, equivalent to margin: 0 auto in CSS.
|
|
3157
|
+
*
|
|
3158
|
+
* The inner table carries the `width` HTML attribute set to the
|
|
3159
|
+
* maxWidth value. Outlook Classic reads `width` as a hard pixel
|
|
3160
|
+
* cap; it has no concept of max-width so this is the only lever
|
|
3161
|
+
* available. Modern clients receive the CSS max-width on the
|
|
3162
|
+
* same table and behave correctly.
|
|
3163
|
+
*
|
|
3164
|
+
* The outer TD remains at width: 100% so it always fills its
|
|
3165
|
+
* parent cell in every client — only the inner content is capped.
|
|
3166
|
+
*/
|
|
3167
|
+
jsx("center", { children: jsx("table", { "aria-label": "Text Max Width Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, width: maxWidth, style: maxWidthTableStyle, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { children: innerContent }) }) }) }) })) : (innerContent);
|
|
3168
|
+
return (jsx("table", { "aria-label": "Text Block Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, ...rootBindingProps(bindings), style: {
|
|
2943
3169
|
width: "100%",
|
|
2944
3170
|
borderCollapse: "collapse",
|
|
2945
|
-
}, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: tdStyle, align: textAlign, children:
|
|
3171
|
+
}, children: jsx("tbody", { children: jsx("tr", { children: jsx("td", { style: tdStyle, align: textAlign, children: href && !devMode ? (jsx("a", { href: href, target: target, ...(target === "_blank" ? { rel: "noopener noreferrer" } : {}), style: {
|
|
3172
|
+
display: "block",
|
|
3173
|
+
textDecoration: "none",
|
|
3174
|
+
color: "inherit",
|
|
3175
|
+
}, children: wrappedContent })) : (wrappedContent) }) }) }) }));
|
|
2946
3176
|
}
|
|
2947
3177
|
var Text_default = memo(Text, arePropsEqual);
|
|
2948
3178
|
|
|
@@ -3068,7 +3298,7 @@ function buildLinkHref(innerLink) {
|
|
|
3068
3298
|
return null;
|
|
3069
3299
|
}
|
|
3070
3300
|
}
|
|
3071
|
-
function Icon({ config, devNode, devMode, children }) {
|
|
3301
|
+
function Icon({ config, devNode, devMode, children, bindings }) {
|
|
3072
3302
|
const {
|
|
3073
3303
|
// base64Source,
|
|
3074
3304
|
width, height, backgroundColor, padding = "0", borderRadius = "0", border, innerLink, justifyContent = "center", } = config;
|
|
@@ -3203,7 +3433,7 @@ function Icon({ config, devNode, devMode, children }) {
|
|
|
3203
3433
|
<!--<![endif]-->
|
|
3204
3434
|
`
|
|
3205
3435
|
: null;
|
|
3206
|
-
return (jsxs("table", { "aria-label": "Icon", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: align, style: {
|
|
3436
|
+
return (jsxs("table", { "aria-label": "Icon", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: align, ...rootBindingProps(bindings), style: {
|
|
3207
3437
|
// --- Start dev
|
|
3208
3438
|
position: "relative",
|
|
3209
3439
|
// --- End dev
|