email-builder-utils 1.1.45 → 1.1.47

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.
Files changed (34) hide show
  1. package/dist/utils/blocks/button.d.ts +29 -0
  2. package/dist/utils/blocks/button.d.ts.map +1 -0
  3. package/dist/utils/blocks/button.js +137 -0
  4. package/dist/utils/blocks/dividers.d.ts +4 -0
  5. package/dist/utils/blocks/dividers.d.ts.map +1 -0
  6. package/dist/utils/blocks/dividers.js +71 -0
  7. package/dist/utils/blocks/grid.d.ts +6 -0
  8. package/dist/utils/blocks/grid.d.ts.map +1 -0
  9. package/dist/utils/blocks/grid.js +248 -0
  10. package/dist/utils/blocks/image.d.ts +8 -0
  11. package/dist/utils/blocks/image.d.ts.map +1 -0
  12. package/dist/utils/blocks/image.js +58 -0
  13. package/dist/utils/blocks/shape.d.ts +2 -0
  14. package/dist/utils/blocks/shape.d.ts.map +1 -0
  15. package/dist/utils/blocks/shape.js +199 -0
  16. package/dist/utils/blocks/text.d.ts +2 -0
  17. package/dist/utils/blocks/text.d.ts.map +1 -0
  18. package/dist/utils/blocks/text.js +106 -0
  19. package/dist/utils/blocks/video.d.ts +2 -0
  20. package/dist/utils/blocks/video.d.ts.map +1 -0
  21. package/dist/utils/blocks/video.js +119 -0
  22. package/dist/utils/buildStyles.d.ts +10 -0
  23. package/dist/utils/buildStyles.d.ts.map +1 -0
  24. package/dist/utils/buildStyles.js +101 -0
  25. package/dist/utils/gradientUtils.d.ts +8 -0
  26. package/dist/utils/gradientUtils.d.ts.map +1 -0
  27. package/dist/utils/gradientUtils.js +68 -0
  28. package/dist/utils/jsonToHTML.d.ts +2 -29
  29. package/dist/utils/jsonToHTML.d.ts.map +1 -1
  30. package/dist/utils/jsonToHTML.js +18 -1490
  31. package/dist/utils/outlookSupport.d.ts +4 -207
  32. package/dist/utils/outlookSupport.d.ts.map +1 -1
  33. package/dist/utils/outlookSupport.js +86 -453
  34. package/package.json +1 -1
@@ -1,479 +1,112 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
2
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.tableCommonStyle = void 0;
18
- exports.computeArcSize = computeArcSize;
19
- exports.shouldHideInOutlook = shouldHideInOutlook;
20
- exports.buildOutlookGridTable = buildOutlookGridTable;
21
- exports.buildOutlookGridCell = buildOutlookGridCell;
22
- exports.buildOutlookEmptyCell = buildOutlookEmptyCell;
23
- exports.buildOutlookVMLImage = buildOutlookVMLImage;
24
- exports.buildOutlookVMLButton = buildOutlookVMLButton;
25
- exports.buildVMLShape = buildVMLShape;
26
- exports.buildOutlookShapeWrapper = buildOutlookShapeWrapper;
27
- exports.buildOutlookVMLVideo = buildOutlookVMLVideo;
28
- exports.buildOutlookVMLVDivider = buildOutlookVMLVDivider;
29
- exports.wrapNonOutlookContent = wrapNonOutlookContent;
30
- exports.buildOutlookWrapperTable = buildOutlookWrapperTable;
31
- exports.buildOutlookImageWrapper = buildOutlookImageWrapper;
32
- exports.buildOutlookButtonWrapper = buildOutlookButtonWrapper;
33
- exports.buildOutlookVideoWrapper = buildOutlookVideoWrapper;
34
- exports.buildOutlookVDividerWrapper = buildOutlookVDividerWrapper;
35
- exports.buildOutlookShapeWrapperTable = buildOutlookShapeWrapperTable;
36
- exports.buildOutlookGridWrapper = buildOutlookGridWrapper;
37
- exports.buildOutlookGridRow = buildOutlookGridRow;
38
- exports.buildOutlookGridCellWrapper = buildOutlookGridCellWrapper;
39
- exports.buildOutlookEmptyCellWrapper = buildOutlookEmptyCellWrapper;
40
- const jimp_1 = require("jimp");
41
- // ============================================================================
42
- // OUTLOOK SUPPORT UTILITIES
43
- // ============================================================================
44
- // These functions handle Microsoft Outlook-specific VML rendering and
45
- // conditional comments for email HTML compatibility.
46
- // ============================================================================
47
- // ---------- Helper: Compute arc size for rounded shapes ----------
48
- function computeArcSize(borderRadius, widthPx) {
49
- if (!borderRadius)
50
- return "0";
51
- if (typeof borderRadius === "number")
52
- return Math.min(borderRadius / widthPx, 1).toFixed(2);
53
- const s = borderRadius.toString().trim();
54
- if (s.endsWith("%")) {
55
- const pct = parseFloat(s.replace("%", "")) || 0;
56
- return Math.min(pct / 100, 1).toFixed(2);
3
+ exports.appendOutlookSupport = appendOutlookSupport;
4
+ exports.appendOutlookForImage = appendOutlookForImage;
5
+ exports.loadImageNaturalDimensions = loadImageNaturalDimensions;
6
+ const buildStyles_1 = require("./buildStyles");
7
+ function appendOutlookSupport(content, contentStyle, className, msoWidth) {
8
+ const visibilityClass = className || "";
9
+ const shouldHideInOutlook = visibilityClass.includes("hide-desktop");
10
+ if (shouldHideInOutlook) {
11
+ return `
12
+ <!--[if !mso]><!-->
13
+ <table data-ebr-role="wrapper" role="presentation" width="100%" style="${buildStyles_1.tableCommonStyle}" class="${visibilityClass}"><tr><td style="${contentStyle}">${content}</td></tr></table>
14
+ <!--<![endif]-->
15
+ `;
57
16
  }
58
- const px = parseFloat(s.replace("px", "")) || 0;
59
- return Math.min(px / widthPx, 1).toFixed(2);
60
- }
61
- // ---------- Helper: Check if should hide in Outlook ----------
62
- function shouldHideInOutlook(visibilityClass) {
63
- return visibilityClass.includes("hide-desktop");
64
- }
65
- function buildOutlookGridTable(options) {
66
- const { columnGap, gridVisibilityClass, tableStyles, content } = options;
67
- // Outlook needs explicit table layout and width attributes
68
- // For MSO, we use a fixed width table to prevent overflow issues
69
- // For non-MSO, we use 100% with max-width for responsiveness
70
- return `<!--[if mso]>
71
- <table border="0" cellpadding="0" cellspacing="${columnGap}" width="600"
72
- style="border-collapse:separate;border-spacing:${columnGap}px;${tableStyles};mso-table-lspace:0;mso-table-rspace:0;"
73
- class="${gridVisibilityClass}">
74
- <![endif]-->
75
- <!--[if !mso]><!-->
76
- <table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%"
77
- role="presentation"
78
- style="border-collapse:separate;border-spacing:${columnGap}px; ${tableStyles}; max-width:600px;overflow:hidden;"
79
- class="${gridVisibilityClass}">
80
- <!--<![endif]-->
81
- ${content}
82
- </table>`;
83
- }
84
- function buildOutlookGridCell(options) {
85
- const { widthPercent, widthPx, verticalAlign, visibilityClass, responsive, styles, content, } = options;
86
- // Outlook needs explicit pixel width on cells, not just percentages
87
- return `<td
88
- width="${Math.round(widthPx)}"
89
- class="${[responsive ? "stack-column" : "", visibilityClass]
90
- .filter(Boolean)
91
- .join(" ")}"
92
- style="vertical-align:${verticalAlign};word-break:break-word;${styles}">
93
- ${content}
94
- </td>`;
95
- }
96
- function buildOutlookEmptyCell(options) {
97
- const { widthPercent, widthPx, responsive } = options;
98
- // Safe empty cell with explicit pixel width for Outlook
99
- return `<td width="${Math.round(widthPx)}"
100
- ${responsive ? 'class="stack-column"' : ""}
101
- style="vertical-align:top;">&nbsp;</td>`;
17
+ if (msoWidth) {
18
+ return `
19
+ <!--[if mso]>
20
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0" width="${msoWidth}" style="border-collapse:collapse;width:${msoWidth}px;"><tr><td width="${msoWidth}" style="${contentStyle}">
21
+ <![endif]-->
22
+ <!--[if !mso]><!-->
23
+ <table data-ebr-role="wrapper" role="presentation" width="100%" border="0" cellpadding="0" cellspacing="0" style="${buildStyles_1.tableCommonStyle}; max-width:600px;" class="${visibilityClass}"><tr><td width="100%" style="${contentStyle}">
24
+ <!--<![endif]-->
25
+ ${content}
26
+ <!--[if mso]></td></tr></table><![endif]-->
27
+ <!--[if !mso]><!-->
28
+ </td></tr></table>
29
+ <!--<![endif]-->
30
+ `;
31
+ }
32
+ return `
33
+ <table data-ebr-role="wrapper" role="presentation" width="100%" border="0" cellpadding="0" cellspacing="0" style="${buildStyles_1.tableCommonStyle}; max-width:600px;" class="${visibilityClass}"><tr><td width="100%" style="${contentStyle}">${content}</td></tr></table>
34
+ `;
102
35
  }
103
- async function buildOutlookVMLImage(options) {
104
- const { imageUrl, outerContainerWidth, innerContainerWidth, style = {} } = options;
105
- const image = await jimp_1.Jimp.read(imageUrl);
106
- const originalWidth = image.bitmap.width;
107
- const originalHeight = image.bitmap.height;
108
- // Use inner container width for proper scaling
109
- const widthScalingFactor = Math.min(innerContainerWidth / originalWidth, 1);
110
- const scaledWidth = Math.round(originalWidth * widthScalingFactor);
111
- const scaledHeight = Math.round(originalHeight * widthScalingFactor);
36
+ async function appendOutlookForImage(content, outerContainerWidth, innerContainerWidth, imageUrl, style = {}, finalWidth, finalHeight) {
37
+ let vmlWidth;
38
+ let vmlHeight;
39
+ if (finalWidth && finalHeight) {
40
+ vmlWidth = finalWidth;
41
+ vmlHeight = finalHeight;
42
+ }
43
+ else if (imageUrl) {
44
+ try {
45
+ const { width: originalWidth, height: originalHeight } = await loadImageNaturalDimensions(imageUrl);
46
+ const widthScalingFactor = Math.min(outerContainerWidth / originalWidth, innerContainerWidth / originalWidth, 1);
47
+ vmlWidth = Math.round(originalWidth * widthScalingFactor);
48
+ vmlHeight = Math.round(originalHeight * widthScalingFactor);
49
+ }
50
+ catch {
51
+ vmlWidth = innerContainerWidth;
52
+ vmlHeight = innerContainerWidth;
53
+ }
54
+ }
55
+ else {
56
+ vmlWidth = innerContainerWidth;
57
+ vmlHeight = innerContainerWidth;
58
+ }
112
59
  const borderWidth = parseInt(style?.borderWidth) || 0;
113
60
  const borderColor = style?.borderColor || "transparent";
114
61
  const borderRadius = parseInt(style?.borderRadius) || 0;
115
62
  const useRoundRect = borderRadius > 0;
116
- const arcsize = useRoundRect
117
- ? Math.min(borderRadius / scaledHeight, 1).toFixed(2)
118
- : "";
63
+ const arcsize = useRoundRect ? Math.min(borderRadius / vmlHeight, 1).toFixed(2) : "";
119
64
  const borderAttributes = borderWidth > 0
120
65
  ? `strokeweight="${borderWidth}px" strokecolor="${borderColor}"`
121
66
  : `stroked="false"`;
122
- return `<!--[if mso]>
123
- <v:${useRoundRect ? "roundrect" : "rect"} xmlns:v="urn:schemas-microsoft-com:vml"
124
- style="width:${scaledWidth}px;height:${scaledHeight}px;max-width:${scaledWidth}px;"
125
- ${borderAttributes}
126
- ${useRoundRect ? `arcsize="${arcsize}"` : ""}
127
- fill="true" fillcolor="none">
128
- <v:fill src="${imageUrl}" type="frame" />
129
- <v:textbox inset="0,0,0,0"><div style="display:none;mso-hide:all;">.</div></v:textbox>
130
- </v:${useRoundRect ? "roundrect" : "rect"}>
67
+ let outlookImage;
68
+ if (useRoundRect && borderRadius > 0) {
69
+ outlookImage = `<!--[if mso]>
70
+ <table border="0" cellpadding="0" cellspacing="0" width="${vmlWidth}" style="width:${vmlWidth}px;">
71
+ <tr>
72
+ <td align="center" valign="top" width="${vmlWidth}" style="width:${vmlWidth}px;">
73
+ <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml"
74
+ style="width:${vmlWidth}px;height:${vmlHeight}px;"
75
+ ${borderAttributes}
76
+ arcsize="${arcsize}"
77
+ fill="true" fillcolor="none">
78
+ <v:fill src="${imageUrl}" type="tile" aspect="atmost" />
79
+ <v:textbox inset="0,0,0,0"><div style="display:none;">.</div></v:textbox>
80
+ </v:roundrect>
81
+ </td>
82
+ </tr>
83
+ </table>
131
84
  <![endif]-->`;
132
- }
133
- function buildOutlookVMLButton(options) {
134
- const { navigateToUrl, text, buttonStyle: { width = 200, height = 44, borderRadius = 0, borderColor = "transparent", borderWidth = 0, buttonColor = "none", buttonPadding = { top: 0, bottom: 0, left: 0, right: 0 }, color = "#000000", fontFamily = "Arial, sans-serif", fontSize = 16, fontWeight = 400, }, } = options;
135
- const borderAttributes = borderWidth > 0
136
- ? `strokeweight="${borderWidth}px" strokecolor="${borderColor}"`
137
- : `stroked="false"`;
138
- return `
139
- <!--[if mso]>
140
- <v:${borderRadius ? "roundrect" : "rect"} xmlns:v="urn:schemas-microsoft-com:vml" href="${navigateToUrl}"
141
- style="height:${height}px;v-text-anchor:middle;width:${width}px;mso-width-relative:page;"
142
- arcsize="${borderRadius / height}" ${borderAttributes}
143
- fillcolor="${buttonColor}">
144
- <w:anchorlock/>
145
- <v:textbox inset="${buttonPadding.top}px,${buttonPadding.left}px,${buttonPadding.bottom}px,${buttonPadding.right}px">
146
- <center style="font-family:${fontFamily};font-size:${fontSize}px;font-weight:${fontWeight};color:${color};">
147
- ${text}
148
- </center>
149
- </v:textbox>
150
- </v:${borderRadius ? "roundrect" : "rect"}>
151
- <![endif]-->
152
- `;
153
- }
154
- function buildVMLShape(options) {
155
- const { shape, widthPx, heightPx, imageUrl, backgroundColor, shapeColor, borderWidth, borderColor, borderRadius, text, textColor = "#000000", textSize = 14, verticalAlign = "middle", textAlign = "center", msoHasBakedText = false, } = options;
156
- const bw = borderWidth || 0;
157
- const bc = borderColor || "transparent";
158
- const borderAttrs = bw > 0 ? `strokeweight="${bw}px" strokecolor="${bc}"` : `stroked="false"`;
159
- const fillColor = shapeColor || backgroundColor || "#2F80ED";
160
- const fillMarkup = `<v:fill ${imageUrl ? `src="${imageUrl}" type="frame" aspect="atleast"` : ""} color="${fillColor}" />`;
161
- let tag = "rect";
162
- let extraAttr = "";
163
- if (shape === "circle" || shape === "oval") {
164
- tag = "oval";
165
- }
166
- else if (shape === "rounded") {
167
- tag = "roundrect";
168
- extraAttr = `arcsize="${computeArcSize(borderRadius, widthPx)}"`;
169
85
  }
170
- const vAlignMap = { top: "top", middle: "middle", bottom: "bottom" };
171
- const hAlignMap = {
172
- left: "left",
173
- center: "center",
174
- right: "right",
175
- justify: "left",
176
- };
177
- const vAlign = vAlignMap[verticalAlign] || "middle";
178
- const hAlign = hAlignMap[textAlign] || "center";
179
- const safeFontSize = Math.max(Math.round(textSize), 10);
180
- const textboxMarkup = text && !msoHasBakedText
181
- ? `<v:textbox inset="6pt,6pt,6pt,6pt" style="mso-fit-shape-to-text:false;">
182
- <div style="display:table;width:100%;height:100%;">
183
- <div style="display:table-cell;vertical-align:${vAlign};text-align:${hAlign};padding:0 6px;">
184
- <div style="color:${textColor};font-family:Arial, sans-serif;font-size:${safeFontSize}px;line-height:1.3;word-wrap:break-word;">
185
- ${text}
186
- </div>
187
- </div>
188
- </div>
189
- </v:textbox>`
190
- : `<v:textbox inset="0,0,0,0"><div style="display:none;mso-hide:all;">.</div></v:textbox>`;
191
- return `
192
- <v:${tag} xmlns:v="urn:schemas-microsoft-com:vml"
193
- style="width:${widthPx}px;height:${heightPx}px;mso-width-relative:page;max-width:${widthPx}px;"
194
- ${borderAttrs}
195
- fill="true" fillcolor="${fillColor}"${extraAttr}>
196
- ${fillMarkup}
197
- ${textboxMarkup}
198
- </v:${tag}>`;
199
- }
200
- function buildOutlookShapeWrapper(options) {
201
- const { visibilityClass, widthPx, heightPx, opts } = options;
202
- const pad = opts.padding || {};
203
- const align = opts.alignment || "left";
204
- const valign = opts.verticalAlign || "middle";
205
- const vml = buildVMLShape({
206
- shape: opts.shape,
207
- widthPx,
208
- heightPx,
209
- imageUrl: opts.msoBakeImageWithText || opts.imageUrl,
210
- backgroundColor: opts.shapeColor || opts.backgroundColor,
211
- borderWidth: opts.borderWidth,
212
- borderColor: opts.borderColor,
213
- borderRadius: opts.borderRadius,
214
- text: opts.text,
215
- textColor: opts.textColor,
216
- textSize: opts.textSize,
217
- verticalAlign: opts.verticalAlign,
218
- textAlign: opts.textAlign,
219
- msoHasBakedText: Boolean(opts.msoBakeImageWithText),
220
- });
221
- if (shouldHideInOutlook(visibilityClass)) {
222
- return `<!--[if !mso]><!-->
223
- <table align="${align}" border="0" cellpadding="0" cellspacing="0"
224
- style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
225
- <tr>
226
- <td valign="${valign}"
227
- style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
228
- ${vml}
229
- </td>
230
- </tr>
231
- </table>
232
- <!--<![endif]-->`;
233
- }
234
- return `<!--[if mso]>
235
- <table align="${align}" border="0" cellpadding="0" cellspacing="0"
236
- style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
86
+ else {
87
+ const borderStyleAttr = borderWidth > 0 ? `border: ${borderWidth}px solid ${borderColor};` : '';
88
+ outlookImage = `<!--[if mso]>
89
+ <table border="0" cellpadding="0" cellspacing="0" width="${vmlWidth}" style="width:${vmlWidth}px;">
237
90
  <tr>
238
- <td valign="${valign}"
239
- style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
240
- ${vml}
91
+ <td align="center" valign="top" width="${vmlWidth}" style="width:${vmlWidth}px;">
92
+ <img src="${imageUrl}" alt="Image" border="0" width="${vmlWidth}" height="${vmlHeight}" style="display:block; width:${vmlWidth}px; height:${vmlHeight}px; max-width:${vmlWidth}px; ${borderStyleAttr}" />
241
93
  </td>
242
94
  </tr>
243
95
  </table>
244
- <![endif]-->`;
245
- }
246
- function buildOutlookVMLVideo(options) {
247
- const { innerContainerWidth, calculatedHeight, videoLink, resolvedThumbnail, borderWidth, borderColor, borderRadius, altText, hideOnDesktop, } = options;
248
- const playIconWidth = 65;
249
- const playIconHeight = 46;
250
- const vmlLeft = innerContainerWidth / 2 - playIconWidth / 2;
251
- const vmlTop = calculatedHeight / 2 - playIconHeight / 2;
252
- if (hideOnDesktop) {
253
- return `<!--[if !mso]><!-->
254
- <v:group xmlns:v="urn:schemas-microsoft-com:vml"
255
- coordsize="${innerContainerWidth},${calculatedHeight}"
256
- href="${videoLink}"
257
- style="width:${innerContainerWidth}px;height:${calculatedHeight}px;mso-width-relative:page;">
258
- <v:rect fill="t" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px;" stroked="t"
259
- strokeweight="${borderWidth}px"
260
- strokecolor="${borderColor}"
261
- ${borderRadius > 0 ? `arcsize="${Math.min(borderRadius / calculatedHeight, 1).toFixed(2)}"` : ""}
262
- >
263
- <v:fill src="${resolvedThumbnail}" type="frame" color="#FFFFFF"/>
264
- </v:rect>
265
- <v:shape type="#_x0000_t75"
266
- style="position:absolute;
267
- left:${vmlLeft.toFixed(1)}px;
268
- top:${vmlTop.toFixed(1)}px;
269
- width:${playIconWidth}px;
270
- height:${playIconHeight}px;"
271
- alt="Play" href="${videoLink}" title="${altText || "Video"}"
272
- stroked="f" filled="t">
273
- <v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
274
- </v:shape>
275
- </v:group>
276
- <!--<![endif]-->`;
277
- }
278
- return `<!--[if mso]>
279
- <v:group xmlns:v="urn:schemas-microsoft-com:vml"
280
- coordsize="${innerContainerWidth},${calculatedHeight}"
281
- href="${videoLink}"
282
- style="width:${innerContainerWidth}px;height:${calculatedHeight}px;mso-width-relative:page;">
283
- <v:rect fill="t" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px; stroked="t"
284
- strokeweight="${borderWidth}px"
285
- strokecolor="${borderColor}"
286
- ${borderRadius > 0 ? `arcsize="${Math.min(borderRadius / calculatedHeight, 1).toFixed(2)}"` : ""}
287
- >
288
- <v:fill src="${resolvedThumbnail}" type="frame" color="#FFFFFF"/>
289
- </v:rect>
290
- <v:shape type="#_x0000_t75"
291
- style="position:absolute;
292
- left:${vmlLeft.toFixed(1)}px;
293
- top:${vmlTop.toFixed(1)}px;
294
- width:${playIconWidth}px;
295
- height:${playIconHeight}px;"
296
- alt="Play" href="${videoLink}" title="${altText || "Video"}"
297
- stroked="f" filled="t">
298
- <v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
299
- </v:shape>
300
- </v:group>
301
- <![endif]-->`;
302
- }
303
- function buildOutlookVMLVDivider(options) {
304
- const { width, height, dividerColor } = options;
305
- return `<!--[if mso | IE]>
306
- <v:rect xmlns:v="urn:schemas-microsoft-com:vml" fillcolor="${dividerColor}" style="width:${width}px;height:${height}px;mso-width-relative:page;" stroke="f"></v:rect>
307
96
  <![endif]-->`;
308
- }
309
- function wrapNonOutlookContent(options) {
310
- const { content, visibilityClass } = options;
311
- return `<!--[if !mso]><!-->
312
- ${content}
313
- <!--<![endif]-->`;
314
- }
315
- // ============================================================================
316
- // OUTLOOK WRAPPER TABLE BUILDER
317
- // ============================================================================
318
- // Replaces the inline Outlook conditional logic in appendOutlookSupport()
319
- exports.tableCommonStyle = "border-collapse:collapse; table-layout:fixed;";
320
- function buildOutlookWrapperTable(options) {
321
- const { content, contentStyle, visibilityClass = "", maxWidth = 600 } = options;
322
- const shouldHideInOutlook = visibilityClass.includes("hide-desktop");
323
- if (shouldHideInOutlook) {
324
- return `
325
- <!--[if !mso]><!-->
326
- <table width="100%" style="${exports.tableCommonStyle}" class="${visibilityClass}"><tr><td style="${contentStyle}">${content}</td></tr></table>
327
- <!--<![endif]-->
328
- `;
329
97
  }
330
98
  return `
331
- <table width="100%" style="${exports.tableCommonStyle}; max-width:${maxWidth}px;" class="${visibilityClass}"><tr><td style="${contentStyle}">${content}</td></tr></table>
332
- `;
333
- }
334
- function buildOutlookImageWrapper(options) {
335
- const { outlookVMLImage, nonMsoWrapper, containerStyles, visibilityClass } = options;
336
- const wrappedOutlookImage = wrapNonOutlookContent({
337
- content: outlookVMLImage,
338
- visibilityClass,
339
- });
340
- const outlookImage = `
341
- ${wrappedOutlookImage}
99
+ ${outlookImage}
342
100
  <!--[if !mso]><!-->
343
- ${nonMsoWrapper}
101
+ ${content}
344
102
  <!--<![endif]-->
345
103
  `;
346
- return buildOutlookWrapperTable({
347
- content: outlookImage,
348
- contentStyle: containerStyles,
349
- visibilityClass,
350
- });
351
104
  }
352
- function buildOutlookButtonWrapper(options) {
353
- const { outlookVMLButton, buttonElement, convertedStyles, visibilityClass } = options;
354
- return buildOutlookWrapperTable({
355
- content: `${outlookVMLButton}${buttonElement}`,
356
- contentStyle: convertedStyles,
357
- visibilityClass,
105
+ async function loadImageNaturalDimensions(imageUrl) {
106
+ return new Promise((resolve, reject) => {
107
+ const img = new Image();
108
+ img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });
109
+ img.onerror = () => reject(new Error(`Failed to load image: ${imageUrl}`));
110
+ img.src = imageUrl;
358
111
  });
359
112
  }
360
- function buildOutlookVideoWrapper(options) {
361
- const { videoContent, outerContainerStyles, cellWidthInPx, visibilityClass, percentWidth, textAlign = "left", } = options;
362
- return `
363
- <!--[if mso]>
364
- <table width="${cellWidthInPx}" border="0" cellpadding="0" cellspacing="0" style="margin:0; padding:0; border-collapse: collapse; ${outerContainerStyles}">
365
- <![endif]-->
366
- <!--[if !mso]><!-->
367
- <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin:0; padding:0; border-collapse: collapse; max-width:${cellWidthInPx}px;" class="${visibilityClass}">
368
- <!--<![endif]-->
369
- <tr>
370
- <td align="${textAlign}" style="padding:0;">
371
- <!--[if mso]>
372
- <table width="${percentWidth}" border="0" cellpadding="0" cellspacing="0" align="${textAlign}" style="border-collapse:collapse;">
373
- <![endif]-->
374
- <!--[if !mso]><!-->
375
- <table border="0" cellpadding="0" cellspacing="0" role="presentation"
376
- align="${textAlign}"
377
- style="margin:0; width:${percentWidth}; border-collapse:collapse;">
378
- <!--<![endif]-->
379
- <tr>
380
- <td align="${textAlign}" style="text-align:${textAlign}; padding:0;">
381
- ${videoContent}
382
- </td>
383
- </tr>
384
- </table>
385
- </td>
386
- </tr>
387
- </table>
388
- `;
389
- }
390
- function buildOutlookVDividerWrapper(options) {
391
- const { outlookVDividerVML, nonMsoDivider, convertedStyle, visibilityClass } = options;
392
- const dividerContent = `
393
- <table cellpadding="0" cellspacing="0" border="0" align="center" style="width:auto; ${convertedStyle}">
394
- <tr>
395
- <td style="vertical-align: middle; text-align: center;">
396
- ${outlookVDividerVML}
397
- <!--[if !mso]><!-- -->
398
- ${nonMsoDivider}
399
- <!--<![endif]-->
400
- </td>
401
- </tr>
402
- </table>
403
- `;
404
- return buildOutlookWrapperTable({
405
- content: dividerContent,
406
- contentStyle: convertedStyle,
407
- visibilityClass,
408
- });
409
- }
410
- function buildOutlookShapeWrapperTable(options) {
411
- const { outlookContent, nonMsoContent, padding, alignment, visibilityClass } = options;
412
- return `
413
- <table width="100%" style="border-collapse:collapse;table-layout:fixed;max-width:600px;" class="${visibilityClass}">
414
- <tr>
415
- <td style="padding:${padding.top || 0}px ${padding.right || 0}px ${padding.bottom || 0}px ${padding.left || 0}px;text-align:${alignment};">
416
- ${outlookContent}
417
- <!--[if !mso]><!-->
418
- ${nonMsoContent}
419
- <!--<![endif]-->
420
- </td>
421
- </tr>
422
- </table>`;
423
- }
424
- function buildOutlookGridWrapper(options) {
425
- const { columnGap, gridVisibilityClass, tableStyles, content, cellWidthInPx } = options;
426
- // Use fixed width for MSO to prevent percentage calculation issues
427
- // Add overflow:hidden and table-layout:fixed for proper constraint
428
- return `<!--[if mso]>
429
- <table border="0" cellpadding="0" cellspacing="${columnGap}" width="${cellWidthInPx}"
430
- style="border-collapse:separate;border-spacing:${columnGap}px;${tableStyles};mso-table-lspace:0;mso-table-rspace:0;overflow:hidden;">
431
- <![endif]-->
432
- <!--[if !mso]><!-->
433
- <table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%"
434
- role="presentation"
435
- style="border-collapse:separate;border-spacing:${columnGap}px; ${tableStyles}; max-width:${cellWidthInPx}px;overflow:hidden;"
436
- class="${gridVisibilityClass}">
437
- <!--<![endif]-->
438
- ${content}
439
- </table>`;
440
- }
441
- function buildOutlookGridRow(options) {
442
- return `<tr>${options.rowContent}</tr>`;
443
- }
444
- function buildOutlookGridCellWrapper(options) {
445
- const { widthPx, widthPercent, verticalAlign, visibilityClass, responsive, styles, msoContent, nonMsoContent, } = options;
446
- // For Outlook, we need explicit pixel widths to prevent percentage calculation issues
447
- // Add overflow:hidden to both MSO and non-MSO cells to prevent overflow
448
- return `<!--[if mso]>
449
- <td width="${widthPx}" valign="${verticalAlign}" style="vertical-align:${verticalAlign};word-break:break-word;overflow:hidden;${styles}">
450
- ${msoContent}
451
- </td>
452
- <![endif]-->
453
- <!--[if !mso]><!-->
454
- <td
455
- width="${widthPercent}%"
456
- class="${[responsive ? "stack-column" : "", visibilityClass]
457
- .filter(Boolean)
458
- .join(" ")}"
459
- style="vertical-align:${verticalAlign};word-break:break-word;overflow:hidden;${styles}">
460
- ${nonMsoContent}
461
- </td>
462
- <!--<![endif]-->`;
463
- }
464
- function buildOutlookEmptyCellWrapper(options) {
465
- const { widthPx, widthPercent, responsive } = options;
466
- // Add overflow:hidden to prevent content from breaking layout in Outlook
467
- return `<!--[if mso]>
468
- <td width="${widthPx}" ${responsive ? 'class="stack-column"' : ""} style="vertical-align:top;overflow:hidden;">&nbsp;</td>
469
- <![endif]-->
470
- <!--[if !mso]><!-->
471
- <td width="${widthPercent}%"
472
- ${responsive ? 'class="stack-column"' : ""}
473
- style="vertical-align:top;overflow:hidden;">&nbsp;</td>
474
- <!--<![endif]-->`;
475
- }
476
- // ============================================================================
477
- // EXPORT INDEX
478
- // ============================================================================
479
- __exportStar(require("./common"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "email-builder-utils",
3
- "version": "1.1.45",
3
+ "version": "1.1.47",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [