email-builder-utils 1.1.23 → 1.1.26

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.
@@ -24,6 +24,5 @@ interface IBlockData {
24
24
  export declare const tableCommonStyle = "border-collapse:collapse; table-layout:fixed;";
25
25
  export declare function convertToHtml(blockData: IBlockData, rootData: any, cellWidthInPx: number): Promise<string>;
26
26
  export declare function convertVideoBlock(blockData: any, cellWidthInPx: number): Promise<string>;
27
- export declare function convertShapeBlock(blockData: IBlockData): Promise<string>;
28
27
  export {};
29
28
  //# sourceMappingURL=jsonToHTML.d.ts.map
@@ -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;CAChB;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;AAOD,eAAO,MAAM,gBAAgB,kDAAkD,CAAC;AAiDhF,wBAAsB,aAAa,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAqB9F;AA2UD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAmI5E;AAqJD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,UAAU,mBAuI5D"}
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;CAChB;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;AAOD,eAAO,MAAM,gBAAgB,kDAAkD,CAAC;AAiDhF,wBAAsB,aAAa,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAqB9F;AAsVD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBAoK5E"}
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.tableCommonStyle = void 0;
4
4
  exports.convertToHtml = convertToHtml;
5
5
  exports.convertVideoBlock = convertVideoBlock;
6
- exports.convertShapeBlock = convertShapeBlock;
7
6
  const jimp_1 = require("jimp");
8
7
  const types_1 = require("../types");
9
8
  const common_1 = require("./common");
@@ -266,9 +265,9 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
266
265
  const visualRows = Math.ceil(total / columns);
267
266
  let html = `
268
267
  <!--[if mso]>
269
- <table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%" style="${exports.tableCommonStyle}">
268
+ <table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%" style="${exports.tableCommonStyle}border-collapse: separate;border-spacing:${columnGap}px;">
270
269
  <![endif]-->
271
- <table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%" role="presentation" style="${exports.tableCommonStyle} ${tableStyles}">
270
+ <table border="0" cellpadding="0" cellspacing="${columnGap}" width="100%" role="presentation" style="${exports.tableCommonStyle} ${tableStyles}border-collapse: separate;border-spacing:${columnGap}px;">
272
271
  `;
273
272
  for (let r = 0; r < visualRows; r++) {
274
273
  html += "<tr>";
@@ -285,13 +284,13 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
285
284
  <td
286
285
  width="${widthPercent}%"
287
286
  ${responsive ? 'class="stack-column"' : ""}
288
- style="vertical-align:${verticalAlign}; padding:0; word-break:break-word; ${styles}"
287
+ style="vertical-align:${verticalAlign}; word-break:break-word; ${styles} "
289
288
  >
290
289
  ${childHtml}
291
290
  </td>`;
292
291
  }
293
292
  else {
294
- html += `<td width="${widthPercent}%" ${responsive ? 'class="stack-column"' : ""} style="padding:0;"></td>`;
293
+ html += `<td width="${widthPercent}%" ${responsive ? 'class="stack-column"' : ""} style=""></td>`;
295
294
  }
296
295
  }
297
296
  html += "</tr>";
@@ -320,7 +319,7 @@ async function convertGridCellBlock(blockData, rootData, cellWidthPercent, paren
320
319
  }
321
320
  async function convertVideoBlock(blockData, cellWidthInPx) {
322
321
  const { style, props } = blockData.data;
323
- const { videoUrl, youtubeVideoUrl, thumbnailUrl, altText } = props || {};
322
+ const { videoUrl, youtubeVideoUrl, thumbnailUrl, altText } = props;
324
323
  const videoLink = youtubeVideoUrl || videoUrl || "#";
325
324
  let resolvedThumbnail = thumbnailUrl || "https://via.placeholder.com/480x360?text=No+Thumbnail";
326
325
  if (youtubeVideoUrl) {
@@ -351,12 +350,19 @@ async function convertVideoBlock(blockData, cellWidthInPx) {
351
350
  else {
352
351
  percentWidth = "100%";
353
352
  }
354
- const innerContainerWidth = (parseFloat(percentWidth) / 100) * (cellWidthInPx - (style?.padding?.left || 0) - (style?.padding?.right || 0));
353
+ const innerContainerWidth = (parseFloat(percentWidth) / 100) *
354
+ (cellWidthInPx -
355
+ (style?.padding?.left || 0) -
356
+ (style?.padding?.right || 0));
355
357
  const aspectRatio = 16 / 9;
356
358
  const calculatedHeight = innerContainerWidth / aspectRatio;
357
359
  const outerContainerStyles = buildStyles({
358
360
  ...style,
359
361
  width: undefined,
362
+ borderColor: undefined,
363
+ borderRadius: undefined,
364
+ borderWidth: undefined,
365
+ borderStyle: undefined,
360
366
  }, {
361
367
  perChanges: addPxOrPerToAttributes,
362
368
  pxChanges: addPxToAttributes,
@@ -371,72 +377,94 @@ async function convertVideoBlock(blockData, cellWidthInPx) {
371
377
  const vmlLeft = innerContainerWidth / 2 - playIconWidth / 2;
372
378
  const vmlTop = calculatedHeight / 2 - playIconHeight / 2;
373
379
  const videoContent = `
374
- <!--[if mso]>
375
- <v:group xmlns:v="urn:schemas-microsoft-com:vml" coordsize="${innerContainerWidth},${calculatedHeight}"
376
- coordorigin="0,0"
377
- href="${videoLink}"
378
- style="width:${innerContainerWidth}px;height:${calculatedHeight}px;">
379
- <v:rect fill="t" stroked="f" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px;">
380
- <v:fill src="${resolvedThumbnail}" type="frame"/>
381
- </v:rect>
382
- <v:shape type="#_x0000_t75"
383
- style="position:absolute;
384
- left:${vmlLeft.toFixed(1)}px;
385
- top:${vmlTop.toFixed(1)}px;
386
- width:${playIconWidth}px;
387
- height:${playIconHeight}px;"
388
- alt="Play" href="${videoLink}" title="${altText || "Video"}"
389
- stroked="f" filled="t">
390
- <v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
391
- </v:shape>
392
- </v:group>
393
- <![endif]-->
394
-
395
- <!--[if !mso]><!-->
396
- <table
397
- width="${innerContainerWidth}"
398
- cellpadding="0"
399
- cellspacing="0"
400
- border="0"
401
- role="presentation"
402
- style="
403
- background-image: url('${resolvedThumbnail}');
404
- background-size: cover;
405
- background-position: center;
406
-
407
- max-width: ${innerContainerWidth}px;
408
- height: ${calculatedHeight}px;
409
- box-sizing: border-box;
410
- "
411
- align="center"
380
+ <!--[if mso]>
381
+ <v:group xmlns:v="urn:schemas-microsoft-com:vml"
382
+ coordsize="${innerContainerWidth},${calculatedHeight}"
383
+ href="${videoLink}"
384
+ style="width:${innerContainerWidth}px;height:${calculatedHeight}px;">
385
+ <v:rect fill="t" style="position:absolute;width:${innerContainerWidth}px;height:${calculatedHeight}px; stroked="t"
386
+ strokeweight="${borderWidth}px"
387
+ strokecolor="${borderColor}"
388
+ ${borderRadius > 0 ? `arcsize="${Math.min(borderRadius / calculatedHeight, 1).toFixed(2)}"` : ""}
412
389
  >
413
- <tr>
414
- <td style="height: ${calculatedHeight}px; padding: 0; text-align: center; vertical-align: middle; border-radius: ${borderRadius}px;
415
- border: ${borderWidth}px solid ${borderColor};" align="center" valign="middle">
416
- <a href="${videoLink}" target="_blank" style="display:inline-block; border: 0; outline: none; text-decoration: none;">
417
- <img
418
- src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png"
419
- width="${playIconWidth}"
420
- alt="Play"
421
- style="display: block; border: 0; outline: none; text-decoration: none; height: auto;"
422
- />
423
- </a>
424
- </td>
425
- </tr>
426
- </table>
427
- <!--<![endif]-->
428
- `;
390
+ <v:fill src="${resolvedThumbnail}" type="frame" color="${style?.backgroundColor || "#FFFFFF"}"/>
391
+ </v:rect>
392
+ <v:shape type="#_x0000_t75"
393
+ style="position:absolute;
394
+ left:${vmlLeft.toFixed(1)}px;
395
+ top:${vmlTop.toFixed(1)}px;
396
+ width:${playIconWidth}px;
397
+ height:${playIconHeight}px;"
398
+ alt="Play" href="${videoLink}" title="${altText || "Video"}"
399
+ stroked="f" filled="t">
400
+ <v:imagedata src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png" />
401
+ </v:shape>
402
+ </v:group>
403
+ <![endif]-->
404
+
405
+ <!--[if !mso]><!-->
406
+ <table
407
+ width="${innerContainerWidth}"
408
+ cellpadding="0"
409
+ cellspacing="0"
410
+ border="0"
411
+ role="presentation"
412
+ align="${style?.textAlign || "left"}"
413
+ style="
414
+ max-width: ${innerContainerWidth}px;
415
+ width: 100%;
416
+ height: ${calculatedHeight}px;
417
+ background-color: ${style?.backgroundColor || "#FFFFFF"};
418
+ background-image: url('${resolvedThumbnail}');
419
+ background-size: cover;
420
+ background-position: center;
421
+ background-repeat: no-repeat;
422
+ box-sizing: border-box;
423
+ border: ${borderWidth}px ${style?.borderStyle || "solid"} ${borderColor};
424
+ border-radius: ${borderRadius}px;
425
+ "
426
+ >
427
+ <tr>
428
+ <td style="padding: 0; height: ${calculatedHeight}px; text-align: center; vertical-align: middle;" valign="middle">
429
+ <a href="${videoLink}" target="_blank" style="display:inline-block; border: 0; outline: none; text-decoration: none;">
430
+ <img
431
+ src="https://app-rsrc.getbee.io/public/resources/components/widgetBar/video-content-icon-sets/light/type-01.png"
432
+ width="${playIconWidth}"
433
+ alt="Play"
434
+ style="display: block;
435
+ border: 0;
436
+ outline: none;
437
+ text-decoration: none;
438
+ height: auto;"
439
+ />
440
+ </a>
441
+ </td>
442
+ </tr>
443
+ </table>
444
+ <!--<![endif]-->
445
+ `;
429
446
  const wrapperHtml = `
430
- <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin:0; padding:0; border-collapse: collapse;">
431
- <tr>
432
- <td align="center" style="padding:0; ${outerContainerStyles}">
433
- <div style="display: inline-block; width: ${percentWidth}; max-width: ${cellWidthInPx}px; box-sizing: border-box;">
434
- ${videoContent}
435
- </div>
436
- </td>
437
- </tr>
438
- </table>
439
- `;
447
+ <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin:0; padding:0; border-collapse: collapse;">
448
+ <tr>
449
+ <td align="${style?.textAlign || "left"}" style="padding:0; ${outerContainerStyles}">
450
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation"
451
+ align="${style?.textAlign || "left"}"
452
+ style="
453
+ margin:0;
454
+ max-width:${cellWidthInPx}px;
455
+ width:${percentWidth};
456
+ border-collapse:collapse;
457
+ ">
458
+ <tr>
459
+ <td align="${style?.textAlign || "left"}" style="text-align:${style?.textAlign || "left"}; padding:0;">
460
+ ${videoContent}
461
+ </td>
462
+ </tr>
463
+ </table>
464
+ </td>
465
+ </tr>
466
+ </table>
467
+ `;
440
468
  return wrapperHtml;
441
469
  }
442
470
  // Enhanced Shape Block HTML Conversion using appendOutlookForShape
@@ -534,24 +562,31 @@ function buildVMLShape({ shape, widthPx, heightPx, imageUrl, backgroundColor, bo
534
562
  // ---------- convertShapeBlock (updated, keeps your structure) ----------
535
563
  async function convertShapeBlock(blockData) {
536
564
  const { style, props } = blockData.data;
537
- const { shape, text, textColor = "#000000", imageUrl } = props || {};
538
- const { width = "100", height = "150", padding = {}, backgroundColor = "#2F80ED", borderRadius, borderWidth = 0, borderStyle = "solid", borderColor = "transparent", customCss, shapeColor, alignment = "left", msoBakeImageWithText, } = style || {};
565
+ const { shape, text, textColor = "#000000", imageUrl } = props;
566
+ const { width = "100", height = "150", padding = {}, backgroundColor = "#2F80ED", borderRadius, borderWidth = 0, borderStyle = "solid", borderColor = "transparent", customCss, shapeColor, alignment = "left", msoBakeImageWithText } = style || {};
539
567
  const borderRadiusMap = {
540
568
  rectangle: "0",
541
569
  rounded: "10px",
542
570
  circle: "50%",
543
- oval: "50%",
571
+ oval: "50%", // Keep this for modern browsers
544
572
  };
545
573
  let resolvedBorderRadius = borderRadius || borderRadiusMap[shape] || "0";
546
- let resolvedWidthPx = typeof width === "number" ? width : parseInt(width.toString().replace("px", ""), 10) || 100;
547
- let resolvedHeightPx = typeof height === "number" ? height : parseInt(height.toString().replace("px", ""), 10) || 150;
548
- // Force circle square
574
+ let resolvedWidthPx = typeof width === "number"
575
+ ? width
576
+ : parseInt(width.toString().replace("px", ""), 10) || 100;
577
+ let resolvedHeightPx = typeof height === "number"
578
+ ? height
579
+ : parseInt(height.toString().replace("px", ""), 10) || 150;
580
+ // Special handling for different shapes
549
581
  if (shape === "circle") {
582
+ // Circle: make it a perfect square with 50% border radius
550
583
  const side = Math.min(resolvedWidthPx, resolvedHeightPx);
551
584
  resolvedWidthPx = side;
552
585
  resolvedHeightPx = side;
553
586
  resolvedBorderRadius = "50%";
554
587
  }
588
+ else if (shape === "oval") {
589
+ }
555
590
  const finalWidthPx = resolvedWidthPx;
556
591
  const finalHeightPx = resolvedHeightPx;
557
592
  const alignmentStyles = {
@@ -563,12 +598,14 @@ async function convertShapeBlock(blockData) {
563
598
  const finalBackgroundColor = shapeColor || backgroundColor;
564
599
  // --- Modern clients content ---
565
600
  let nonMsoContent = "";
601
+ // For modern browsers, use CSS border-radius
602
+ const modernBorderRadius = shape === "oval" ? "50%" : resolvedBorderRadius;
566
603
  // Case 1: Image + Text → use background-image
567
604
  if (imageUrl && text) {
568
605
  nonMsoContent = `
569
606
  <div style="display:inline-block;width:${finalWidthPx}px;height:${finalHeightPx}px;
570
607
  border:${borderWidth}px ${borderStyle} ${borderColor};
571
- border-radius:${resolvedBorderRadius};
608
+ border-radius:${modernBorderRadius};
572
609
  background:${finalBackgroundColor} url('${imageUrl}') center/cover no-repeat;
573
610
  overflow:hidden;${alignmentStyle}${customCss || ""}">
574
611
  <div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;">
@@ -584,11 +621,11 @@ async function convertShapeBlock(blockData) {
584
621
  nonMsoContent = `
585
622
  <div style="display:inline-block;width:${finalWidthPx}px;height:${finalHeightPx}px;
586
623
  border:${borderWidth}px ${borderStyle} ${borderColor};
587
- border-radius:${resolvedBorderRadius};
624
+ border-radius:${modernBorderRadius};
588
625
  overflow:hidden;${alignmentStyle}${customCss || ""}">
589
626
  <img src="${imageUrl}" alt="${text || "Shape image"}"
590
627
  width="${finalWidthPx}" height="${finalHeightPx}"
591
- style="width:100%;height:100%;object-fit:cover;border-radius:${resolvedBorderRadius};display:block;" />
628
+ style="width:100%;height:100%;object-fit:cover;border-radius:${modernBorderRadius};display:block;" />
592
629
  </div>`;
593
630
  }
594
631
  // Case 3: No image → solid background
@@ -597,7 +634,7 @@ async function convertShapeBlock(blockData) {
597
634
  <div style="display:inline-block;width:${finalWidthPx}px;height:${finalHeightPx}px;
598
635
  background:${finalBackgroundColor};
599
636
  border:${borderWidth}px ${borderStyle} ${borderColor};
600
- border-radius:${resolvedBorderRadius};
637
+ border-radius:${modernBorderRadius};
601
638
  ${alignmentStyle}${customCss || ""}">
602
639
  <div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;
603
640
  color:${textColor};text-align:center;padding:8px;box-sizing:border-box;word-break:break-word;">
@@ -619,7 +656,7 @@ async function convertShapeBlock(blockData) {
619
656
  textColor,
620
657
  alignment,
621
658
  padding,
622
- msoBakeImageWithText,
659
+ msoBakeImageWithText
623
660
  });
624
661
  // Wrap in container table
625
662
  const containerTable = `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "email-builder-utils",
3
- "version": "1.1.23",
3
+ "version": "1.1.26",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [