email-builder-utils 1.1.28 → 1.1.30
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/types/Template.d.ts +2 -0
- package/dist/types/Template.d.ts.map +1 -1
- package/dist/utils/common.d.ts +4 -0
- package/dist/utils/common.d.ts.map +1 -1
- package/dist/utils/common.js +12 -0
- package/dist/utils/jsonToHTML.d.ts +2 -0
- package/dist/utils/jsonToHTML.d.ts.map +1 -1
- package/dist/utils/jsonToHTML.js +437 -155
- package/package.json +1 -1
package/dist/types/Template.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Template.d.ts","sourceRoot":"","sources":["../../src/types/Template.ts"],"names":[],"mappings":"AAAA,oBAAY,SAAS;IACnB,IAAI,SAAS;IACb,KAAK,UAAU;IACf,MAAM,WAAW;IACjB,IAAI,YAAY;IAChB,KAAK,UAAU;IACf,QAAQ,WAAW;IACnB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,WAAW,gBAAgB;IAC3B,KAAK,UAAU;IACf,KAAK,UAAU;IACf,QAAQ,aAAa;CACtB;AAED,oBAAY,UAAU;IACpB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,WAAW,gBAAgB;CAC5B;AAED,UAAU,MAAM;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"Template.d.ts","sourceRoot":"","sources":["../../src/types/Template.ts"],"names":[],"mappings":"AAAA,oBAAY,SAAS;IACnB,IAAI,SAAS;IACb,KAAK,UAAU;IACf,MAAM,WAAW;IACjB,IAAI,YAAY;IAChB,KAAK,UAAU;IACf,QAAQ,WAAW;IACnB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,WAAW,gBAAgB;IAC3B,KAAK,UAAU;IACf,KAAK,UAAU;IACf,QAAQ,aAAa;CACtB;AAED,oBAAY,UAAU;IACpB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,WAAW,gBAAgB;CAC5B;AAED,UAAU,MAAM;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,UAAU,MAAM;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;CACH"}
|
package/dist/utils/common.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export declare const extractYouTubeId: (url: string) => string | null;
|
|
2
2
|
export declare const extractVimeoId: (url: string) => string | null;
|
|
3
|
+
export declare function getVisibilityClass(props?: {
|
|
4
|
+
hideOnDesktop?: boolean;
|
|
5
|
+
hideOnMobile?: boolean;
|
|
6
|
+
}): string;
|
|
3
7
|
//# sourceMappingURL=common.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/utils/common.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAqBvD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAIrD,CAAC"}
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/utils/common.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAqBvD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAIrD,CAAC;AAGF,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,UASA"}
|
package/dist/utils/common.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.extractVimeoId = exports.extractYouTubeId = void 0;
|
|
4
|
+
exports.getVisibilityClass = getVisibilityClass;
|
|
4
5
|
const extractYouTubeId = (url) => {
|
|
5
6
|
try {
|
|
6
7
|
const u = new URL(url);
|
|
@@ -31,3 +32,14 @@ const extractVimeoId = (url) => {
|
|
|
31
32
|
return match ? match[1] : null;
|
|
32
33
|
};
|
|
33
34
|
exports.extractVimeoId = extractVimeoId;
|
|
35
|
+
function getVisibilityClass(props) {
|
|
36
|
+
if (!props)
|
|
37
|
+
return "";
|
|
38
|
+
const { hideOnDesktop, hideOnMobile } = props;
|
|
39
|
+
return [
|
|
40
|
+
hideOnMobile ? "hide-mobile" : "",
|
|
41
|
+
hideOnDesktop ? "hide-desktop" : "",
|
|
42
|
+
]
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
.join(" ");
|
|
45
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonToHTML.d.ts","sourceRoot":"","sources":["../../src/utils/jsonToHTML.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAUrC,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"jsonToHTML.d.ts","sourceRoot":"","sources":["../../src/utils/jsonToHTML.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAUrC,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE;QACJ,KAAK,EAAE,cAAc,CAAC;QACtB,KAAK,EAAE,GAAG,CAAC;QACX,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;KAC7B,CAAC;CACH;AAYD,eAAO,MAAM,gBAAgB,kDAAkD,CAAC;AA2DhF,wBAAsB,aAAa,CACjC,SAAS,EAAE,UAAU,EACrB,QAAQ,EAAE,GAAG,EACb,aAAa,EAAE,MAAM,mBAwBtB;AAuiBD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAyL5E"}
|
package/dist/utils/jsonToHTML.js
CHANGED
|
@@ -84,34 +84,67 @@ async function convertToHtml(blockData, rootData, cellWidthInPx) {
|
|
|
84
84
|
return "";
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
-
function appendOutlookSupport(content, contentStyle) {
|
|
87
|
+
// function appendOutlookSupport(content: string, contentStyle: string) {
|
|
88
|
+
// return `
|
|
89
|
+
// <table width="100%" style="${tableCommonStyle}"><tr><td style="${contentStyle}">${content}</td></tr></table>
|
|
90
|
+
// `;
|
|
91
|
+
// }
|
|
92
|
+
function appendOutlookSupport(content, contentStyle, className) {
|
|
93
|
+
const visibilityClass = className || "";
|
|
94
|
+
const shouldHideInOutlook = visibilityClass.includes("hide-desktop");
|
|
95
|
+
if (shouldHideInOutlook) {
|
|
96
|
+
return `
|
|
97
|
+
<!--[if !mso]><!-->
|
|
98
|
+
<table width="100%" style="${exports.tableCommonStyle}" class="${visibilityClass}"><tr><td style="${contentStyle}">${content}</td></tr></table>
|
|
99
|
+
<!--<![endif]-->
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
88
102
|
return `
|
|
89
|
-
<table width="100%" style="${exports.tableCommonStyle}"><tr><td style="${contentStyle}">${content}</td></tr></table>
|
|
103
|
+
<table width="100%" style="${exports.tableCommonStyle}" class="${visibilityClass}"><tr><td style="${contentStyle}">${content}</td></tr></table>
|
|
90
104
|
`;
|
|
91
105
|
}
|
|
92
106
|
function convertDividerBlockToHtml(blockData) {
|
|
93
|
-
const { style } = blockData.data;
|
|
94
|
-
const {
|
|
107
|
+
const { style, props } = blockData.data;
|
|
108
|
+
const { hideOnMobile, hideOnDesktop } = props;
|
|
109
|
+
const { thickness, dividerColor, width, ...rest } = style;
|
|
95
110
|
const convertedStyle = buildStyles(rest, {
|
|
96
111
|
perChanges: [],
|
|
97
112
|
pxChanges: allPxAttributes,
|
|
98
113
|
});
|
|
114
|
+
const dividerWidth = width || "100%";
|
|
115
|
+
// Build class name based on visibility
|
|
116
|
+
const visibilityClass = [
|
|
117
|
+
hideOnMobile ? "hide-mobile" : "",
|
|
118
|
+
hideOnDesktop ? "hide-desktop" : "",
|
|
119
|
+
]
|
|
120
|
+
.filter(Boolean)
|
|
121
|
+
.join(" ");
|
|
99
122
|
const dividerContent = `
|
|
100
|
-
<table
|
|
123
|
+
<table
|
|
124
|
+
width="${dividerWidth}%"
|
|
125
|
+
cellpadding="0"
|
|
126
|
+
cellspacing="0"
|
|
127
|
+
>
|
|
101
128
|
<tr>
|
|
102
|
-
<td
|
|
129
|
+
<td
|
|
130
|
+
height="${thickness}"
|
|
131
|
+
style="font-size:1px; line-height:1px; background:${dividerColor}; width:${dividerWidth};"
|
|
132
|
+
>
|
|
133
|
+
|
|
134
|
+
</td>
|
|
103
135
|
</tr>
|
|
104
136
|
</table>
|
|
105
137
|
`;
|
|
106
|
-
return appendOutlookSupport(dividerContent, convertedStyle);
|
|
138
|
+
return appendOutlookSupport(dividerContent, convertedStyle, visibilityClass);
|
|
107
139
|
}
|
|
108
140
|
function convertSpacerBlockToHtml(blockData) {
|
|
109
|
-
const { style } = blockData.data;
|
|
141
|
+
const { style, props } = blockData.data;
|
|
142
|
+
const visibilityClass = (0, common_1.getVisibilityClass)(props);
|
|
110
143
|
const styles = buildStyles(style, {
|
|
111
144
|
perChanges: [],
|
|
112
145
|
pxChanges: allPxAttributes,
|
|
113
146
|
});
|
|
114
|
-
return appendOutlookSupport(``, styles);
|
|
147
|
+
return appendOutlookSupport(``, styles, visibilityClass);
|
|
115
148
|
}
|
|
116
149
|
// function convertTextBlock(blockData: IBlockData) {
|
|
117
150
|
// const { style, props } = blockData.data;
|
|
@@ -131,6 +164,7 @@ function convertSpacerBlockToHtml(blockData) {
|
|
|
131
164
|
// }
|
|
132
165
|
function convertTextBlock(blockData) {
|
|
133
166
|
const { style, props } = blockData.data;
|
|
167
|
+
const visibilityClass = (0, common_1.getVisibilityClass)(props);
|
|
134
168
|
const { width, backgroundColor, padding, borderRadius, borderStyle, borderColor, borderWidth, textContainerBackgroundColor, textContainerPadding, ...rest } = style;
|
|
135
169
|
const textBoxStyle = {
|
|
136
170
|
width,
|
|
@@ -158,9 +192,9 @@ function convertTextBlock(blockData) {
|
|
|
158
192
|
.replaceAll(/<\/p>/g, "</div>");
|
|
159
193
|
const navigateToUrl = props.navigateToUrl || "";
|
|
160
194
|
const convertedTextBox = `<div style="display: inline-block; max-width: 100%; box-sizing: border-box; ${convertedTextStyle}">${sanitizedText.replaceAll(/\n/g, "<br>")}</div>`;
|
|
161
|
-
const textContent = appendOutlookSupport(convertedTextBox, styles);
|
|
195
|
+
const textContent = appendOutlookSupport(convertedTextBox, styles, visibilityClass);
|
|
162
196
|
return navigateToUrl
|
|
163
|
-
? `<a href="${navigateToUrl}" rel="noreferrer noopener" style="color:inherit;
|
|
197
|
+
? `<a href="${navigateToUrl}" rel="noreferrer noopener" style="color:inherit;text-decoration:none;cursor:pointer;">${textContent}</a>`
|
|
164
198
|
: textContent;
|
|
165
199
|
}
|
|
166
200
|
async function appendOutlookForImage(content, outerContainerWidth, innerContainerWidth, imageUrl, style = {}) {
|
|
@@ -197,13 +231,20 @@ async function appendOutlookForImage(content, outerContainerWidth, innerContaine
|
|
|
197
231
|
<!--<![endif]-->
|
|
198
232
|
`;
|
|
199
233
|
}
|
|
234
|
+
async function computeScaledDimensions(imageUrl, maxContainerWidthPx) {
|
|
235
|
+
const image = await jimp_1.Jimp.read(imageUrl);
|
|
236
|
+
const originalWidth = image.bitmap.width;
|
|
237
|
+
const originalHeight = image.bitmap.height;
|
|
238
|
+
const widthScalingFactor = Math.min(maxContainerWidthPx / originalWidth, 1);
|
|
239
|
+
const scaledWidth = Math.round(originalWidth * widthScalingFactor);
|
|
240
|
+
const scaledHeight = Math.round(originalHeight * widthScalingFactor);
|
|
241
|
+
return { originalWidth, originalHeight, scaledWidth, scaledHeight };
|
|
242
|
+
}
|
|
200
243
|
async function convertImageBlock(blockData, cellWidthInPx) {
|
|
201
244
|
const { style, props } = blockData.data;
|
|
202
245
|
const { altText, imageUrl, navigateToUrl } = props;
|
|
246
|
+
const visibilityClass = (0, common_1.getVisibilityClass)(props);
|
|
203
247
|
const { width, height, objectFit, borderRadius, borderWidth, borderColor, borderStyle, ...containerStyle } = style;
|
|
204
|
-
const image = await jimp_1.Jimp.read(imageUrl);
|
|
205
|
-
const originalWidth = image.bitmap.width;
|
|
206
|
-
const originalHeight = image.bitmap.height;
|
|
207
248
|
// Ensure border styles are applied only to the container, not the image
|
|
208
249
|
const imageStyle = {
|
|
209
250
|
width,
|
|
@@ -212,27 +253,37 @@ async function convertImageBlock(blockData, cellWidthInPx) {
|
|
|
212
253
|
borderStyle,
|
|
213
254
|
borderRadius: borderRadius,
|
|
214
255
|
borderColor,
|
|
215
|
-
maxWidth: `${originalWidth}px`, // Limit to original size
|
|
216
|
-
maxHeight: `${originalHeight}px`,
|
|
217
256
|
};
|
|
218
257
|
// Add border styles to container for fallback clients
|
|
219
258
|
const containerStyles = buildStyles({
|
|
220
259
|
...containerStyle,
|
|
221
260
|
}, { perChanges: [], pxChanges: addPxToAttributes });
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
pxChanges: addPxToAttributes,
|
|
225
|
-
});
|
|
226
|
-
const imageElement = `<img src="${imageUrl}" alt="${altText}" style="${imageTagStyles}; max-width: ${originalWidth}px; max-height: ${originalHeight}px;" />`;
|
|
227
|
-
const innerContainerWidth = ((typeof width === "string" ? parseInt(width.replace("%", "")) : width) /
|
|
261
|
+
const innerContainerWidth = (((typeof width === "string" ? parseInt(width.replace("%", "")) : width) ||
|
|
262
|
+
100) /
|
|
228
263
|
100) *
|
|
229
264
|
(cellWidthInPx -
|
|
230
265
|
(style?.padding?.left || 0) -
|
|
231
266
|
(style?.padding?.right || 0));
|
|
232
|
-
const
|
|
233
|
-
const
|
|
267
|
+
const { originalWidth, originalHeight, scaledWidth, scaledHeight } = await computeScaledDimensions(imageUrl, innerContainerWidth);
|
|
268
|
+
const imageTagStyles = buildStyles({
|
|
269
|
+
maxWidth: `${originalWidth}px`, // Limit to original size
|
|
270
|
+
maxHeight: `${originalHeight}px`,
|
|
271
|
+
...imageStyle,
|
|
272
|
+
}, {
|
|
273
|
+
perChanges: addPxOrPerToAttributes,
|
|
274
|
+
pxChanges: addPxToAttributes,
|
|
275
|
+
});
|
|
276
|
+
const imageElement = `<img src="${imageUrl}" alt="${altText}" width="${scaledWidth}" height="${scaledHeight}" style="${imageTagStyles}; width:100%; height:auto; max-width:${originalWidth}px; max-height:${originalHeight}px;" />`;
|
|
277
|
+
const percentWidth = typeof width === "string" && width.endsWith("%")
|
|
278
|
+
? width
|
|
279
|
+
: typeof width === "number"
|
|
280
|
+
? `${width}%`
|
|
281
|
+
: "100%";
|
|
282
|
+
const nonMsoWrapper = `<div style="display:inline-block; width:${percentWidth}; max-width:${originalWidth}px;">${imageElement}</div>`;
|
|
283
|
+
const outlookImage = await appendOutlookForImage(nonMsoWrapper, cellWidthInPx, innerContainerWidth, imageUrl, style);
|
|
284
|
+
const imageContent = appendOutlookSupport(outlookImage, containerStyles, visibilityClass);
|
|
234
285
|
return navigateToUrl
|
|
235
|
-
? `<a href="${navigateToUrl}" target="_blank" rel="noreferrer noopener"
|
|
286
|
+
? `<a href="${navigateToUrl}" target="_blank" rel="noreferrer noopener" style="display:block;">${imageContent}</a>`
|
|
236
287
|
: imageContent;
|
|
237
288
|
}
|
|
238
289
|
function appendOutlookForButton(content, buttonStyle, navigateToUrl, text) {
|
|
@@ -262,6 +313,7 @@ function appendOutlookForButton(content, buttonStyle, navigateToUrl, text) {
|
|
|
262
313
|
function convertButtonBlock(blockData) {
|
|
263
314
|
const { style, props } = blockData.data;
|
|
264
315
|
const { text, navigateToUrl } = props;
|
|
316
|
+
const visibilityClass = (0, common_1.getVisibilityClass)(props);
|
|
265
317
|
const { fontFamily, fontSize, fontWeight, borderColor, borderRadius, borderWidth, borderStyle, buttonPadding, color, buttonColor, width, height, ...rest } = style;
|
|
266
318
|
const buttonStyle = {
|
|
267
319
|
width,
|
|
@@ -286,44 +338,71 @@ function convertButtonBlock(blockData) {
|
|
|
286
338
|
pxChanges: allPxAttributes,
|
|
287
339
|
});
|
|
288
340
|
const buttonElement = `<a href="${navigateToUrl}" rel="noreferrer noopener" style="display:inline-block; text-decoration:none; cursor:pointer;"><button style="${convertedButtonStyle}">${text}</button></a>`;
|
|
289
|
-
const buttonContent = appendOutlookSupport(appendOutlookForButton(buttonElement, style, navigateToUrl, text), convertedStyles);
|
|
341
|
+
const buttonContent = appendOutlookSupport(appendOutlookForButton(buttonElement, style, navigateToUrl, text), convertedStyles, visibilityClass);
|
|
290
342
|
return buttonContent;
|
|
291
343
|
}
|
|
292
344
|
async function convertGridBlock(blockData, rootData, cellWidthInPx) {
|
|
293
345
|
const { style = {}, childrenIds = [], props } = blockData.data;
|
|
294
346
|
const { columns = 1, cellWidths = [], responsive = true } = props;
|
|
295
347
|
const { columnGap = 0, ...restStyle } = style;
|
|
348
|
+
const gridVisibilityClass = (0, common_1.getVisibilityClass)(props);
|
|
296
349
|
const tableStyles = buildStyles(restStyle, {
|
|
297
350
|
perChanges: [],
|
|
298
351
|
pxChanges: allPxAttributes,
|
|
299
352
|
});
|
|
300
353
|
const total = childrenIds.length;
|
|
301
354
|
const visualRows = Math.ceil(total / columns);
|
|
355
|
+
// Fix: Calculate visible cells per row to adjust widths
|
|
302
356
|
let html = `
|
|
303
357
|
<!--[if mso]>
|
|
304
|
-
<table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%"
|
|
358
|
+
<table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%"
|
|
359
|
+
style="${exports.tableCommonStyle}border-collapse: separate;border-spacing:${columnGap}px;" class="${gridVisibilityClass}">
|
|
305
360
|
<![endif]-->
|
|
306
|
-
<table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%" role="presentation"
|
|
361
|
+
<table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%" role="presentation"
|
|
362
|
+
style="${exports.tableCommonStyle} ${tableStyles}border-collapse: separate;border-spacing:${columnGap}px;" class="${gridVisibilityClass}">
|
|
307
363
|
`;
|
|
308
364
|
for (let r = 0; r < visualRows; r++) {
|
|
309
365
|
html += "<tr>";
|
|
366
|
+
let visibleCellsInRow = 0;
|
|
367
|
+
const rowCellVisibility = [];
|
|
368
|
+
for (let c = 0; c < columns; c++) {
|
|
369
|
+
const idx = r * columns + c;
|
|
370
|
+
const childId = childrenIds[idx];
|
|
371
|
+
if (childId) {
|
|
372
|
+
const child = rootData[childId];
|
|
373
|
+
const { props: childProps = {} } = child.data || {};
|
|
374
|
+
const isVisible = !childProps.hideOnDesktop;
|
|
375
|
+
rowCellVisibility.push(isVisible);
|
|
376
|
+
if (isVisible)
|
|
377
|
+
visibleCellsInRow++;
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
rowCellVisibility.push(false);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
const widthPerVisibleCell = visibleCellsInRow > 0 ? 100 / visibleCellsInRow : 100 / columns;
|
|
310
384
|
for (let c = 0; c < columns; c++) {
|
|
311
385
|
const idx = r * columns + c;
|
|
312
386
|
const childId = childrenIds[idx];
|
|
313
|
-
const widthPercent = cellWidths[c] ??
|
|
387
|
+
const widthPercent = cellWidths[c] ?? widthPerVisibleCell;
|
|
314
388
|
if (childId) {
|
|
315
389
|
const child = rootData[childId];
|
|
316
|
-
const { style: cellStyle = {} } = child.data || {};
|
|
390
|
+
const { style: cellStyle = {}, props: childProps = {} } = child.data || {};
|
|
317
391
|
const verticalAlign = cellStyle.verticalAlign || "top";
|
|
318
392
|
const { html: childHtml, styles } = await convertGridCellBlock(child, rootData, widthPercent, cellWidthInPx);
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
393
|
+
const cellVisibilityClass = (0, common_1.getVisibilityClass)(childProps);
|
|
394
|
+
if (!childProps.hideOnDesktop) {
|
|
395
|
+
html += `
|
|
396
|
+
<td
|
|
397
|
+
width="${widthPercent}%"
|
|
398
|
+
class="${[responsive ? "stack-column" : "", cellVisibilityClass]
|
|
399
|
+
.filter(Boolean)
|
|
400
|
+
.join(" ")}"
|
|
401
|
+
style="vertical-align:${verticalAlign}; word-break:break-word; ${styles}"
|
|
402
|
+
>
|
|
403
|
+
${childHtml}
|
|
404
|
+
</td>`;
|
|
405
|
+
}
|
|
327
406
|
}
|
|
328
407
|
else {
|
|
329
408
|
html += `<td width="${widthPercent}%" ${responsive ? 'class="stack-column"' : ""} style=""></td>`;
|
|
@@ -335,7 +414,7 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
|
|
|
335
414
|
return html;
|
|
336
415
|
}
|
|
337
416
|
async function convertGridCellBlock(blockData, rootData, cellWidthPercent, parentCellWidthPx) {
|
|
338
|
-
const { style = {}, childrenIds = [] } = blockData.data;
|
|
417
|
+
const { style = {}, childrenIds = [], props = {} } = blockData.data;
|
|
339
418
|
const styles = buildStyles(style, {
|
|
340
419
|
perChanges: [],
|
|
341
420
|
pxChanges: allPxAttributes,
|
|
@@ -348,13 +427,17 @@ async function convertGridCellBlock(blockData, rootData, cellWidthPercent, paren
|
|
|
348
427
|
innerHtmlParts.push(await convertToHtml(child, rootData, cellWidthPx));
|
|
349
428
|
}
|
|
350
429
|
}
|
|
430
|
+
const cellContent = innerHtmlParts.join("");
|
|
351
431
|
return {
|
|
352
|
-
html:
|
|
432
|
+
html: cellContent,
|
|
353
433
|
styles,
|
|
354
434
|
};
|
|
355
435
|
}
|
|
436
|
+
// Enhanced Video Block HTML Conversion with centered play button
|
|
356
437
|
async function convertVideoBlock(blockData, cellWidthInPx) {
|
|
357
438
|
const { style, props } = blockData.data;
|
|
439
|
+
const visibilityClass = (0, common_1.getVisibilityClass)(props);
|
|
440
|
+
const { hideOnDesktop } = props; // Get the hideOnDesktop prop
|
|
358
441
|
const { videoUrl, youtubeVideoUrl, thumbnailUrl, altText } = props;
|
|
359
442
|
const videoLink = youtubeVideoUrl || videoUrl || "#";
|
|
360
443
|
let resolvedThumbnail = thumbnailUrl || "https://via.placeholder.com/480x360?text=No+Thumbnail";
|
|
@@ -386,10 +469,7 @@ async function convertVideoBlock(blockData, cellWidthInPx) {
|
|
|
386
469
|
else {
|
|
387
470
|
percentWidth = "100%";
|
|
388
471
|
}
|
|
389
|
-
const innerContainerWidth = (parseFloat(percentWidth) / 100) *
|
|
390
|
-
(cellWidthInPx -
|
|
391
|
-
(style?.padding?.left || 0) -
|
|
392
|
-
(style?.padding?.right || 0));
|
|
472
|
+
const innerContainerWidth = (parseFloat(percentWidth) / 100) * (cellWidthInPx - (style?.padding?.left || 0) - (style?.padding?.right || 0));
|
|
393
473
|
const aspectRatio = 16 / 9;
|
|
394
474
|
const calculatedHeight = innerContainerWidth / aspectRatio;
|
|
395
475
|
const outerContainerStyles = buildStyles({
|
|
@@ -412,77 +492,99 @@ async function convertVideoBlock(blockData, cellWidthInPx) {
|
|
|
412
492
|
// VML centering math (for Outlook)
|
|
413
493
|
const vmlLeft = innerContainerWidth / 2 - playIconWidth / 2;
|
|
414
494
|
const vmlTop = calculatedHeight / 2 - playIconHeight / 2;
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
495
|
+
const shouldHideInOutlook = hideOnDesktop;
|
|
496
|
+
const outlookVideoContent = shouldHideInOutlook
|
|
497
|
+
? `<!--[if !mso]><!-->
|
|
498
|
+
<v:group xmlns:v="urn:schemas-microsoft-com:vml"
|
|
499
|
+
coordsize="${innerContainerWidth},${calculatedHeight}"
|
|
500
|
+
href="${videoLink}"
|
|
501
|
+
style="width:${innerContainerWidth}px;height:${calculatedHeight}px;">
|
|
502
|
+
<v:rect fill="t" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px; stroked="t"
|
|
503
|
+
strokeweight="${borderWidth}px"
|
|
504
|
+
strokecolor="${borderColor}"
|
|
505
|
+
${borderRadius > 0 ? `arcsize="${Math.min(borderRadius / calculatedHeight, 1).toFixed(2)}"` : ""}
|
|
506
|
+
>
|
|
507
|
+
<v:fill src="${resolvedThumbnail}" type="frame" color="${style?.backgroundColor || "#FFFFFF"}"/>
|
|
508
|
+
</v:rect>
|
|
509
|
+
<v:shape type="#_x0000_t75"
|
|
510
|
+
style="position:absolute;
|
|
511
|
+
left:${vmlLeft.toFixed(1)}px;
|
|
512
|
+
top:${vmlTop.toFixed(1)}px;
|
|
513
|
+
width:${playIconWidth}px;
|
|
514
|
+
height:${playIconHeight}px;"
|
|
515
|
+
alt="Play" href="${videoLink}" title="${altText || "Video"}"
|
|
516
|
+
stroked="f" filled="t">
|
|
517
|
+
<v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
|
|
518
|
+
</v:shape>
|
|
519
|
+
</v:group>
|
|
520
|
+
<!--<![endif]-->`
|
|
521
|
+
: `<!--[if mso]>
|
|
522
|
+
<v:group xmlns:v="urn:schemas-microsoft-com:vml"
|
|
523
|
+
coordsize="${innerContainerWidth},${calculatedHeight}"
|
|
524
|
+
href="${videoLink}"
|
|
525
|
+
style="width:${innerContainerWidth}px;height:${calculatedHeight}px;">
|
|
526
|
+
<v:rect fill="t" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px; stroked="t"
|
|
527
|
+
strokeweight="${borderWidth}px"
|
|
528
|
+
strokecolor="${borderColor}"
|
|
529
|
+
${borderRadius > 0 ? `arcsize="${Math.min(borderRadius / calculatedHeight, 1).toFixed(2)}"` : ""}
|
|
530
|
+
>
|
|
531
|
+
<v:fill src="${resolvedThumbnail}" type="frame" color="${style?.backgroundColor || "#FFFFFF"}"/>
|
|
532
|
+
</v:rect>
|
|
533
|
+
<v:shape type="#_x0000_t75"
|
|
534
|
+
style="position:absolute;
|
|
535
|
+
left:${vmlLeft.toFixed(1)}px;
|
|
536
|
+
top:${vmlTop.toFixed(1)}px;
|
|
537
|
+
width:${playIconWidth}px;
|
|
538
|
+
height:${playIconHeight}px;"
|
|
539
|
+
alt="Play" href="${videoLink}" title="${altText || "Video"}"
|
|
540
|
+
stroked="f" filled="t">
|
|
541
|
+
<v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
|
|
542
|
+
</v:shape>
|
|
543
|
+
</v:group>
|
|
544
|
+
<![endif]-->`;
|
|
545
|
+
const nonOutlookVideoContent = `<!--[if !mso]><!-->
|
|
546
|
+
<table
|
|
547
|
+
width="${innerContainerWidth}"
|
|
548
|
+
cellpadding="0"
|
|
549
|
+
cellspacing="0"
|
|
550
|
+
border="0"
|
|
551
|
+
role="presentation"
|
|
552
|
+
align="${style?.textAlign || "left"}"
|
|
553
|
+
style="
|
|
554
|
+
max-width: ${innerContainerWidth}px;
|
|
555
|
+
width: 100%;
|
|
556
|
+
height: ${calculatedHeight}px;
|
|
557
|
+
background-color: ${style?.backgroundColor || "#FFFFFF"};
|
|
558
|
+
background-image: url('${resolvedThumbnail}');
|
|
559
|
+
background-size: cover;
|
|
560
|
+
background-position: center;
|
|
561
|
+
background-repeat: no-repeat;
|
|
562
|
+
box-sizing: border-box;
|
|
563
|
+
border: ${borderWidth}px ${style?.borderStyle || "solid"} ${borderColor};
|
|
564
|
+
border-radius: ${borderRadius}px;
|
|
565
|
+
"
|
|
427
566
|
>
|
|
428
|
-
<
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
cellspacing="0"
|
|
448
|
-
border="0"
|
|
449
|
-
role="presentation"
|
|
450
|
-
align="${style?.textAlign || "left"}"
|
|
451
|
-
style="
|
|
452
|
-
max-width: ${innerContainerWidth}px;
|
|
453
|
-
width: 100%;
|
|
454
|
-
height: ${calculatedHeight}px;
|
|
455
|
-
background-color: ${style?.backgroundColor || "#FFFFFF"};
|
|
456
|
-
background-image: url('${resolvedThumbnail}');
|
|
457
|
-
background-size: cover;
|
|
458
|
-
background-position: center;
|
|
459
|
-
background-repeat: no-repeat;
|
|
460
|
-
box-sizing: border-box;
|
|
461
|
-
border: ${borderWidth}px ${style?.borderStyle || "solid"} ${borderColor};
|
|
462
|
-
border-radius: ${borderRadius}px;
|
|
463
|
-
"
|
|
464
|
-
>
|
|
465
|
-
<tr>
|
|
466
|
-
<td style="padding: 0; height: ${calculatedHeight}px; text-align: center; vertical-align: middle;" valign="middle">
|
|
467
|
-
<a href="${videoLink}" target="_blank" style="display:inline-block; border: 0; outline: none; text-decoration: none;">
|
|
468
|
-
<img
|
|
469
|
-
src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png"
|
|
470
|
-
width="${playIconWidth}"
|
|
471
|
-
alt="Play"
|
|
472
|
-
style="display: block;
|
|
473
|
-
border: 0;
|
|
474
|
-
outline: none;
|
|
475
|
-
text-decoration: none;
|
|
476
|
-
height: auto;"
|
|
477
|
-
/>
|
|
478
|
-
</a>
|
|
479
|
-
</td>
|
|
480
|
-
</tr>
|
|
481
|
-
</table>
|
|
482
|
-
<!--<![endif]-->
|
|
483
|
-
`;
|
|
567
|
+
<tr>
|
|
568
|
+
<td style="padding: 0; height: ${calculatedHeight}px; text-align: center; vertical-align: middle;" valign="middle">
|
|
569
|
+
<a href="${videoLink}" target="_blank" style="display:inline-block; border: 0; outline: none; text-decoration: none;">
|
|
570
|
+
<img
|
|
571
|
+
src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png"
|
|
572
|
+
width="${playIconWidth}"
|
|
573
|
+
alt="Play"
|
|
574
|
+
style="display: block;
|
|
575
|
+
border: 0;
|
|
576
|
+
outline: none;
|
|
577
|
+
text-decoration: none;
|
|
578
|
+
height: auto;"
|
|
579
|
+
/>
|
|
580
|
+
</a>
|
|
581
|
+
</td>
|
|
582
|
+
</tr>
|
|
583
|
+
</table>
|
|
584
|
+
<!--<![endif]-->`;
|
|
585
|
+
const videoContent = `${outlookVideoContent}${nonOutlookVideoContent}`;
|
|
484
586
|
const wrapperHtml = `
|
|
485
|
-
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin:0; padding:0; border-collapse: collapse;">
|
|
587
|
+
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin:0; padding:0; border-collapse: collapse;" class="${visibilityClass}">
|
|
486
588
|
<tr>
|
|
487
589
|
<td align="${style?.textAlign || "left"}" style="padding:0; ${outerContainerStyles}">
|
|
488
590
|
<table border="0" cellpadding="0" cellspacing="0" role="presentation"
|
|
@@ -522,10 +624,158 @@ function computeArcSize(borderRadius, widthPx) {
|
|
|
522
624
|
return Math.min(px / widthPx, 1).toFixed(2);
|
|
523
625
|
}
|
|
524
626
|
// ---------- Updated convertShapeBlock function ----------
|
|
627
|
+
// async function convertShapeBlock(blockData: IBlockData) {
|
|
628
|
+
// const { style, props } = blockData.data;
|
|
629
|
+
// const { shape, text, imageUrl } = props as any;
|
|
630
|
+
// const {
|
|
631
|
+
// width = "100",
|
|
632
|
+
// height = "150",
|
|
633
|
+
// padding = {},
|
|
634
|
+
// backgroundColor = "#2F80ED",
|
|
635
|
+
// borderRadius,
|
|
636
|
+
// borderWidth = 0,
|
|
637
|
+
// borderStyle = "solid",
|
|
638
|
+
// borderColor = "transparent",
|
|
639
|
+
// customCss,
|
|
640
|
+
// shapeColor,
|
|
641
|
+
// alignment = "left",
|
|
642
|
+
// msoBakeImageWithText,
|
|
643
|
+
// color = "#000000",
|
|
644
|
+
// fontSize = 14,
|
|
645
|
+
// verticalAlign = "center",
|
|
646
|
+
// } = style || {};
|
|
647
|
+
// const borderRadiusMap: Record<string, string> = {
|
|
648
|
+
// rectangle: "0",
|
|
649
|
+
// rounded: "10px",
|
|
650
|
+
// circle: "50%",
|
|
651
|
+
// oval: "50%",
|
|
652
|
+
// };
|
|
653
|
+
// let resolvedBorderRadius = borderRadius || borderRadiusMap[shape] || "0";
|
|
654
|
+
// let resolvedWidthPx =
|
|
655
|
+
// typeof width === "number"
|
|
656
|
+
// ? width
|
|
657
|
+
// : parseInt(width.toString().replace("px", ""), 10) || 100;
|
|
658
|
+
// let resolvedHeightPx =
|
|
659
|
+
// typeof height === "number"
|
|
660
|
+
// ? height
|
|
661
|
+
// : parseInt(height.toString().replace("px", ""), 10) || 150;
|
|
662
|
+
// // --- Shape specific constraints ---
|
|
663
|
+
// if (shape === "circle") {
|
|
664
|
+
// const side = Math.min(resolvedWidthPx, resolvedHeightPx);
|
|
665
|
+
// resolvedWidthPx = side;
|
|
666
|
+
// resolvedHeightPx = side;
|
|
667
|
+
// resolvedBorderRadius = "50%";
|
|
668
|
+
// } else if (shape === "oval") {
|
|
669
|
+
// resolvedBorderRadius = "50% / 50%";
|
|
670
|
+
// }
|
|
671
|
+
// const finalBackgroundColor = shapeColor || backgroundColor;
|
|
672
|
+
// const alignmentStyles = {
|
|
673
|
+
// left: "margin-right:auto;margin-left:0;",
|
|
674
|
+
// center: "margin-left:auto;margin-right:auto;",
|
|
675
|
+
// right: "margin-left:auto;margin-right:0;",
|
|
676
|
+
// };
|
|
677
|
+
// const alignmentStyle =
|
|
678
|
+
// alignmentStyles[alignment as keyof typeof alignmentStyles] || "";
|
|
679
|
+
// const verticalAlignStyles = {
|
|
680
|
+
// top: "align-items:flex-start;padding-top:8px;",
|
|
681
|
+
// center: "align-items:center;",
|
|
682
|
+
// bottom: "align-items:flex-end;padding-bottom:8px;",
|
|
683
|
+
// };
|
|
684
|
+
// const verticalAlignStyle =
|
|
685
|
+
// verticalAlignStyles[verticalAlign as keyof typeof verticalAlignStyles] ||
|
|
686
|
+
// verticalAlignStyles.center;
|
|
687
|
+
// // Text styling (safe across clients)
|
|
688
|
+
// const textSizeStyle = `font-size:${fontSize}px;line-height:1.3;word-break:break-word;overflow-wrap:break-word;text-align:center;color:${color};`;
|
|
689
|
+
// // ============================
|
|
690
|
+
// // Modern HTML (non-MSO)
|
|
691
|
+
// // ============================
|
|
692
|
+
// let nonMsoContent = "";
|
|
693
|
+
// if (imageUrl && text) {
|
|
694
|
+
// nonMsoContent = `
|
|
695
|
+
// <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
696
|
+
// border:${borderWidth}px ${borderStyle} ${borderColor};
|
|
697
|
+
// border-radius:${resolvedBorderRadius};
|
|
698
|
+
// background:${finalBackgroundColor} url('${imageUrl}') center/cover no-repeat;
|
|
699
|
+
// overflow:hidden;${alignmentStyle}${customCss || ""}">
|
|
700
|
+
// <div style="width:100%;height:100%;display:flex;${verticalAlignStyle}justify-content:center;overflow:hidden;">
|
|
701
|
+
// <div style="${textSizeStyle}padding:6px;max-width:90%;-webkit-line-clamp:3;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden;">
|
|
702
|
+
// ${text}
|
|
703
|
+
// </div>
|
|
704
|
+
// </div>
|
|
705
|
+
// </div>`;
|
|
706
|
+
// } else if (imageUrl) {
|
|
707
|
+
// nonMsoContent = `
|
|
708
|
+
// <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
709
|
+
// border:${borderWidth}px ${borderStyle} ${borderColor};
|
|
710
|
+
// border-radius:${resolvedBorderRadius};
|
|
711
|
+
// overflow:hidden;${alignmentStyle}${customCss || ""}">
|
|
712
|
+
// <img src="${imageUrl}" alt="${text || "shape image"}"
|
|
713
|
+
// width="${resolvedWidthPx}" height="${resolvedHeightPx}"
|
|
714
|
+
// style="width:100%;height:100%;object-fit:cover;border-radius:${resolvedBorderRadius};display:block;" />
|
|
715
|
+
// </div>`;
|
|
716
|
+
// } else {
|
|
717
|
+
// const circlePadding =
|
|
718
|
+
// shape === "circle" ? Math.round(resolvedHeightPx * 0.15) : 8;
|
|
719
|
+
// nonMsoContent = `
|
|
720
|
+
// <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
721
|
+
// background:${finalBackgroundColor};
|
|
722
|
+
// border:${borderWidth}px ${borderStyle} ${borderColor};
|
|
723
|
+
// border-radius:${resolvedBorderRadius};
|
|
724
|
+
// overflow:hidden;${alignmentStyle}${customCss || ""}">
|
|
725
|
+
// <div style="width:100%;height:100%;display:flex;${verticalAlignStyle}justify-content:center;padding:${circlePadding}px;box-sizing:border-box;">
|
|
726
|
+
// <div style="${textSizeStyle}max-width:90%;overflow:hidden;">
|
|
727
|
+
// ${text || ""}
|
|
728
|
+
// </div>
|
|
729
|
+
// </div>
|
|
730
|
+
// </div>`;
|
|
731
|
+
// }
|
|
732
|
+
// // ============================
|
|
733
|
+
// // Outlook (VML) version
|
|
734
|
+
// // ============================
|
|
735
|
+
// const outlookContent = await appendOutlookForShape(
|
|
736
|
+
// nonMsoContent,
|
|
737
|
+
// resolvedWidthPx,
|
|
738
|
+
// resolvedWidthPx,
|
|
739
|
+
// {
|
|
740
|
+
// shape,
|
|
741
|
+
// imageUrl,
|
|
742
|
+
// backgroundColor,
|
|
743
|
+
// shapeColor,
|
|
744
|
+
// borderWidth,
|
|
745
|
+
// borderColor,
|
|
746
|
+
// borderRadius: resolvedBorderRadius,
|
|
747
|
+
// heightPx: resolvedHeightPx,
|
|
748
|
+
// text,
|
|
749
|
+
// textColor: color,
|
|
750
|
+
// textSize: fontSize,
|
|
751
|
+
// verticalAlign,
|
|
752
|
+
// alignment,
|
|
753
|
+
// padding,
|
|
754
|
+
// msoBakeImageWithText,
|
|
755
|
+
// }
|
|
756
|
+
// );
|
|
757
|
+
// // ============================
|
|
758
|
+
// // Final combined block
|
|
759
|
+
// // ============================
|
|
760
|
+
// return `
|
|
761
|
+
// <table width="100%" style="border-collapse:collapse;table-layout:fixed;">
|
|
762
|
+
// <tr>
|
|
763
|
+
// <td style="padding:${padding.top || 0}px ${padding.right || 0}px ${
|
|
764
|
+
// padding.bottom || 0
|
|
765
|
+
// }px ${padding.left || 0}px;text-align:${alignment};">
|
|
766
|
+
// ${outlookContent}
|
|
767
|
+
// <!--[if !mso]><!-->
|
|
768
|
+
// ${nonMsoContent}
|
|
769
|
+
// <!--<![endif]-->
|
|
770
|
+
// </td>
|
|
771
|
+
// </tr>
|
|
772
|
+
// </table>`;
|
|
773
|
+
// }
|
|
525
774
|
async function convertShapeBlock(blockData) {
|
|
526
775
|
const { style, props } = blockData.data;
|
|
527
776
|
const { shape, text, imageUrl } = props;
|
|
528
|
-
const
|
|
777
|
+
const visibilityClass = (0, common_1.getVisibilityClass)(props);
|
|
778
|
+
const { width = "100", height = "150", padding = {}, backgroundColor = "#2F80ED", borderRadius, borderWidth = 0, borderStyle = "solid", borderColor = "transparent", customCss, shapeColor, alignment = "left", msoBakeImageWithText, color = "#000000", fontSize = 14, textAlign = "center", verticalAlign = "middle", } = style || {};
|
|
529
779
|
const borderRadiusMap = {
|
|
530
780
|
rectangle: "0",
|
|
531
781
|
rounded: "10px",
|
|
@@ -539,7 +789,7 @@ async function convertShapeBlock(blockData) {
|
|
|
539
789
|
let resolvedHeightPx = typeof height === "number"
|
|
540
790
|
? height
|
|
541
791
|
: parseInt(height.toString().replace("px", ""), 10) || 150;
|
|
542
|
-
// --- Shape
|
|
792
|
+
// --- Shape-specific constraints ---
|
|
543
793
|
if (shape === "circle") {
|
|
544
794
|
const side = Math.min(resolvedWidthPx, resolvedHeightPx);
|
|
545
795
|
resolvedWidthPx = side;
|
|
@@ -550,25 +800,38 @@ async function convertShapeBlock(blockData) {
|
|
|
550
800
|
resolvedBorderRadius = "50% / 50%";
|
|
551
801
|
}
|
|
552
802
|
const finalBackgroundColor = shapeColor || backgroundColor;
|
|
803
|
+
// --- Horizontal alignment for outer container ---
|
|
553
804
|
const alignmentStyles = {
|
|
554
805
|
left: "margin-right:auto;margin-left:0;",
|
|
555
806
|
center: "margin-left:auto;margin-right:auto;",
|
|
556
807
|
right: "margin-left:auto;margin-right:0;",
|
|
557
808
|
};
|
|
558
809
|
const alignmentStyle = alignmentStyles[alignment] || "";
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
810
|
+
// --- Text + vertical alignment maps ---
|
|
811
|
+
const textAlignMap = {
|
|
812
|
+
left: "left",
|
|
813
|
+
center: "center",
|
|
814
|
+
right: "right",
|
|
815
|
+
justify: "justify",
|
|
563
816
|
};
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
817
|
+
const textAlignStyle = textAlignMap[textAlign] || "center";
|
|
818
|
+
const flexJustify = textAlign === "left"
|
|
819
|
+
? "flex-start"
|
|
820
|
+
: textAlign === "right"
|
|
821
|
+
? "flex-end"
|
|
822
|
+
: "center";
|
|
823
|
+
const flexAlign = verticalAlign === "top"
|
|
824
|
+
? "flex-start"
|
|
825
|
+
: verticalAlign === "bottom"
|
|
826
|
+
? "flex-end"
|
|
827
|
+
: "center";
|
|
828
|
+
// --- Text styling ---
|
|
829
|
+
const textSizeStyle = `font-size:${fontSize}px;line-height:1.3;word-break:break-word;overflow-wrap:break-word;color:${color};`;
|
|
568
830
|
// ============================
|
|
569
831
|
// Modern HTML (non-MSO)
|
|
570
832
|
// ============================
|
|
571
833
|
let nonMsoContent = "";
|
|
834
|
+
// --- Case 1: Image + Text ---
|
|
572
835
|
if (imageUrl && text) {
|
|
573
836
|
nonMsoContent = `
|
|
574
837
|
<div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
@@ -576,13 +839,14 @@ async function convertShapeBlock(blockData) {
|
|
|
576
839
|
border-radius:${resolvedBorderRadius};
|
|
577
840
|
background:${finalBackgroundColor} url('${imageUrl}') center/cover no-repeat;
|
|
578
841
|
overflow:hidden;${alignmentStyle}${customCss || ""}">
|
|
579
|
-
<div style="width:100%;height:100%;display:flex
|
|
580
|
-
<div style="${textSizeStyle}
|
|
842
|
+
<div style="width:100%;height:100%;display:flex;justify-content:${flexJustify};align-items:${flexAlign};overflow:hidden;padding:6px;box-sizing:border-box;">
|
|
843
|
+
<div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">
|
|
581
844
|
${text}
|
|
582
845
|
</div>
|
|
583
846
|
</div>
|
|
584
847
|
</div>`;
|
|
585
848
|
}
|
|
849
|
+
// --- Case 2: Image only ---
|
|
586
850
|
else if (imageUrl) {
|
|
587
851
|
nonMsoContent = `
|
|
588
852
|
<div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
@@ -594,24 +858,22 @@ async function convertShapeBlock(blockData) {
|
|
|
594
858
|
style="width:100%;height:100%;object-fit:cover;border-radius:${resolvedBorderRadius};display:block;" />
|
|
595
859
|
</div>`;
|
|
596
860
|
}
|
|
861
|
+
// --- Case 3: Text only ---
|
|
597
862
|
else {
|
|
598
|
-
const circlePadding = shape === "circle" ? Math.round(resolvedHeightPx * 0.15) : 8;
|
|
599
863
|
nonMsoContent = `
|
|
600
864
|
<div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;
|
|
601
865
|
background:${finalBackgroundColor};
|
|
602
866
|
border:${borderWidth}px ${borderStyle} ${borderColor};
|
|
603
867
|
border-radius:${resolvedBorderRadius};
|
|
604
868
|
overflow:hidden;${alignmentStyle}${customCss || ""}">
|
|
605
|
-
<div style="width:100%;height:100%;display:flex
|
|
606
|
-
<div style="${textSizeStyle}max-width:90%;overflow:hidden;">
|
|
869
|
+
<div style="width:100%;height:100%;display:flex;justify-content:${flexJustify};align-items:${flexAlign};padding:8px;box-sizing:border-box;">
|
|
870
|
+
<div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">
|
|
607
871
|
${text || ""}
|
|
608
872
|
</div>
|
|
609
873
|
</div>
|
|
610
874
|
</div>`;
|
|
611
875
|
}
|
|
612
|
-
//
|
|
613
|
-
// Outlook (VML) version
|
|
614
|
-
// ============================
|
|
876
|
+
// Outlook (VML) fallback
|
|
615
877
|
const outlookContent = await appendOutlookForShape(nonMsoContent, resolvedWidthPx, resolvedWidthPx, {
|
|
616
878
|
shape,
|
|
617
879
|
imageUrl,
|
|
@@ -625,15 +887,14 @@ async function convertShapeBlock(blockData) {
|
|
|
625
887
|
textColor: color,
|
|
626
888
|
textSize: fontSize,
|
|
627
889
|
verticalAlign,
|
|
890
|
+
textAlign, // ✅ added
|
|
628
891
|
alignment,
|
|
629
892
|
padding,
|
|
630
893
|
msoBakeImageWithText,
|
|
631
|
-
});
|
|
632
|
-
//
|
|
633
|
-
// Final combined block
|
|
634
|
-
// ============================
|
|
894
|
+
}, visibilityClass);
|
|
895
|
+
// Combine into table wrapper
|
|
635
896
|
return `
|
|
636
|
-
<table width="100%" style="border-collapse:collapse;table-layout:fixed;">
|
|
897
|
+
<table width="100%" style="border-collapse:collapse;table-layout:fixed;" class="${visibilityClass}">
|
|
637
898
|
<tr>
|
|
638
899
|
<td style="padding:${padding.top || 0}px ${padding.right || 0}px ${padding.bottom || 0}px ${padding.left || 0}px;text-align:${alignment};">
|
|
639
900
|
${outlookContent}
|
|
@@ -644,15 +905,14 @@ async function convertShapeBlock(blockData) {
|
|
|
644
905
|
</tr>
|
|
645
906
|
</table>`;
|
|
646
907
|
}
|
|
647
|
-
// ---------- Updated VML builder
|
|
648
|
-
function buildVMLShape({ shape, widthPx, heightPx, imageUrl, backgroundColor, borderWidth, borderColor, borderRadius, text, textColor = "#000000", textSize = 14, verticalAlign = "center", msoHasBakedText = false, }) {
|
|
649
|
-
// --- Basic setup ---
|
|
908
|
+
// ---------- Updated VML builder ----------
|
|
909
|
+
function buildVMLShape({ shape, widthPx, heightPx, imageUrl, backgroundColor, borderWidth, borderColor, borderRadius, text, textColor = "#000000", textSize = 14, verticalAlign = "middle", textAlign = "center", msoHasBakedText = false, }) {
|
|
650
910
|
const bw = borderWidth || 0;
|
|
651
911
|
const bc = borderColor || "transparent";
|
|
652
912
|
const borderAttrs = bw > 0 ? `strokeweight="${bw}px" strokecolor="${bc}"` : `stroked="false"`;
|
|
653
913
|
const fillColor = backgroundColor || "#2F80ED";
|
|
914
|
+
// Use frame for img fill so sizing is preserved
|
|
654
915
|
const fillMarkup = `<v:fill ${imageUrl ? `src="${imageUrl}" type="frame" aspect="atleast"` : ""} color="${fillColor}" />`;
|
|
655
|
-
// --- Shape tag ---
|
|
656
916
|
let tag = "rect";
|
|
657
917
|
let extraAttr = "";
|
|
658
918
|
if (shape === "circle" || shape === "oval") {
|
|
@@ -662,15 +922,22 @@ function buildVMLShape({ shape, widthPx, heightPx, imageUrl, backgroundColor, bo
|
|
|
662
922
|
tag = "roundrect";
|
|
663
923
|
extraAttr = `arcsize="${computeArcSize(borderRadius, widthPx)}"`;
|
|
664
924
|
}
|
|
665
|
-
//
|
|
666
|
-
const vAlignMap = { top: "top",
|
|
925
|
+
// maps for vml
|
|
926
|
+
const vAlignMap = { top: "top", middle: "middle", bottom: "bottom" };
|
|
927
|
+
const hAlignMap = {
|
|
928
|
+
left: "left",
|
|
929
|
+
center: "center",
|
|
930
|
+
right: "right",
|
|
931
|
+
justify: "left",
|
|
932
|
+
}; // justify -> left fallback in VML
|
|
667
933
|
const vAlign = vAlignMap[verticalAlign] || "middle";
|
|
668
|
-
const
|
|
669
|
-
|
|
934
|
+
const hAlign = hAlignMap[textAlign] || "center";
|
|
935
|
+
const safeFontSize = Math.max(Math.round(textSize), 10);
|
|
936
|
+
// Build the textbox with table/cell for reliable vertical centering in Outlook
|
|
670
937
|
const textboxMarkup = text && !msoHasBakedText
|
|
671
938
|
? `<v:textbox inset="6pt,6pt,6pt,6pt" style="mso-fit-shape-to-text:false;">
|
|
672
939
|
<div style="display:table;width:100%;height:100%;">
|
|
673
|
-
<div style="display:table-cell;vertical-align:${vAlign};text-align:
|
|
940
|
+
<div style="display:table-cell;vertical-align:${vAlign};text-align:${hAlign};padding:0 6px;">
|
|
674
941
|
<div style="color:${textColor};font-family:Arial, sans-serif;font-size:${safeFontSize}px;line-height:1.3;word-wrap:break-word;">
|
|
675
942
|
${text}
|
|
676
943
|
</div>
|
|
@@ -678,20 +945,17 @@ function buildVMLShape({ shape, widthPx, heightPx, imageUrl, backgroundColor, bo
|
|
|
678
945
|
</div>
|
|
679
946
|
</v:textbox>`
|
|
680
947
|
: `<v:textbox inset="0,0,0,0"><div style="display:none;">.</div></v:textbox>`;
|
|
681
|
-
//
|
|
948
|
+
// Return VML shape
|
|
682
949
|
return `
|
|
683
950
|
<v:${tag} xmlns:v="urn:schemas-microsoft-com:vml"
|
|
684
|
-
style="width:${widthPx}px;height:${heightPx}px;
|
|
685
|
-
mso-position-horizontal:center;
|
|
686
|
-
mso-position-vertical:center;"
|
|
951
|
+
style="width:${widthPx}px;height:${heightPx}px;display:inline-block;"
|
|
687
952
|
${borderAttrs}
|
|
688
953
|
fill="true" fillcolor="${fillColor}"${extraAttr}>
|
|
689
954
|
${fillMarkup}
|
|
690
955
|
${textboxMarkup}
|
|
691
956
|
</v:${tag}>`;
|
|
692
957
|
}
|
|
693
|
-
|
|
694
|
-
async function appendOutlookForShape(content, outerContainerWidth, innerContainerWidth, opts) {
|
|
958
|
+
function appendOutlookForShape(content, outerContainerWidth, innerContainerWidth, opts, visibilityClass) {
|
|
695
959
|
const widthPx = Math.round(Math.min(outerContainerWidth, innerContainerWidth));
|
|
696
960
|
const heightPx = Math.max(1, Math.round(opts.heightPx));
|
|
697
961
|
const vml = buildVMLShape({
|
|
@@ -706,14 +970,31 @@ async function appendOutlookForShape(content, outerContainerWidth, innerContaine
|
|
|
706
970
|
text: opts.text,
|
|
707
971
|
textColor: opts.textColor,
|
|
708
972
|
textSize: opts.textSize,
|
|
973
|
+
verticalAlign: opts.verticalAlign,
|
|
974
|
+
textAlign: opts.textAlign,
|
|
709
975
|
msoHasBakedText: Boolean(opts.msoBakeImageWithText),
|
|
710
976
|
});
|
|
711
977
|
const pad = opts.padding || {};
|
|
712
978
|
const align = opts.alignment || "left";
|
|
713
979
|
const valign = opts.verticalAlign || "middle";
|
|
980
|
+
const shouldHideInOutlook = visibilityClass.includes("hide-desktop");
|
|
981
|
+
// Fix: Properly handle Outlook visibility with conditional comments
|
|
982
|
+
if (shouldHideInOutlook) {
|
|
983
|
+
return `<!--[if !mso]><!-->
|
|
984
|
+
<table align="${align}" border="0" cellpadding="0" cellspacing="0"
|
|
985
|
+
style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
|
|
986
|
+
<tr>
|
|
987
|
+
<td valign="${valign}"
|
|
988
|
+
style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
|
|
989
|
+
${vml}
|
|
990
|
+
</td>
|
|
991
|
+
</tr>
|
|
992
|
+
</table>
|
|
993
|
+
<!--<![endif]-->`;
|
|
994
|
+
}
|
|
714
995
|
return `<!--[if mso]>
|
|
715
996
|
<table align="${align}" border="0" cellpadding="0" cellspacing="0"
|
|
716
|
-
style="width:${widthPx}px;height:${heightPx}px;">
|
|
997
|
+
style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
|
|
717
998
|
<tr>
|
|
718
999
|
<td valign="${valign}"
|
|
719
1000
|
style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
|
|
@@ -721,11 +1002,12 @@ async function appendOutlookForShape(content, outerContainerWidth, innerContaine
|
|
|
721
1002
|
</td>
|
|
722
1003
|
</tr>
|
|
723
1004
|
</table>
|
|
724
|
-
|
|
1005
|
+
<![endif]-->`;
|
|
725
1006
|
}
|
|
726
1007
|
function convertVerticalDividerBlockToHtml(blockData) {
|
|
727
|
-
const { style } = blockData.data;
|
|
1008
|
+
const { style, props } = blockData.data;
|
|
728
1009
|
const { width, height, dividerColor, ...rest } = style;
|
|
1010
|
+
const visibilityClass = (0, common_1.getVisibilityClass)(props);
|
|
729
1011
|
// Convert other styles to inline-safe HTML attributes
|
|
730
1012
|
const convertedStyle = buildStyles(rest, {
|
|
731
1013
|
perChanges: [],
|
|
@@ -746,5 +1028,5 @@ function convertVerticalDividerBlockToHtml(blockData) {
|
|
|
746
1028
|
</tr>
|
|
747
1029
|
</table>
|
|
748
1030
|
`;
|
|
749
|
-
return appendOutlookSupport(dividerContent, convertedStyle);
|
|
1031
|
+
return appendOutlookSupport(dividerContent, convertedStyle, visibilityClass);
|
|
750
1032
|
}
|