email-builder-utils 1.1.22 → 1.1.24

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.
@@ -26,6 +26,10 @@ interface IProps {
26
26
  altText: string;
27
27
  cellWidths: number[];
28
28
  responsive?: boolean;
29
+ videoUrl?: string;
30
+ youtubeVideoUrl?: string;
31
+ thumbnailUrl?: string;
32
+ shape?: string;
29
33
  }
30
34
  interface IStyle {
31
35
  [key: string]: any;
@@ -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;CAChB;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;CACtB;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"}
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;CAChB;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;CAChB;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"}
@@ -8,6 +8,10 @@ interface BlockJsonProps {
8
8
  altText: string;
9
9
  imageUrl: string;
10
10
  responsive?: boolean;
11
+ videoUrl?: string;
12
+ youtubeVideoUrl?: string;
13
+ thumbnailUrl?: string;
14
+ shape?: string;
11
15
  }
12
16
  interface IBlockData {
13
17
  type: BlockType;
@@ -20,6 +24,5 @@ interface IBlockData {
20
24
  export declare const tableCommonStyle = "border-collapse:collapse; table-layout:fixed;";
21
25
  export declare function convertToHtml(blockData: IBlockData, rootData: any, cellWidthInPx: number): Promise<string>;
22
26
  export declare function convertVideoBlock(blockData: any, cellWidthInPx: number): Promise<string>;
23
- export declare function convertShapeBlock(blockData: IBlockData): Promise<string>;
24
27
  export {};
25
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;CACtB;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,mBAsBtB;AA2ZD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,mBA0I5E;AA0JD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,UAAU,mBAgJ5D"}
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,mBAgJ5E"}
@@ -3,16 +3,10 @@ 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");
10
- const addPxToAttributes = [
11
- "fontSize",
12
- "lineHeight",
13
- "borderRadius",
14
- "borderWidth",
15
- ];
9
+ const addPxToAttributes = ["fontSize", "lineHeight", "borderRadius", "borderWidth"];
16
10
  const addPxOrPerToAttributes = ["width", "height"];
17
11
  const allPxAttributes = [...addPxToAttributes, ...addPxOrPerToAttributes];
18
12
  exports.tableCommonStyle = "border-collapse:collapse; table-layout:fixed;";
@@ -43,8 +37,7 @@ function buildStyles(style, { pxChanges, perChanges }) {
43
37
  return;
44
38
  if (value === undefined || value === null || value === "")
45
39
  return;
46
- if ((key === "padding" || key === "buttonPadding") &&
47
- typeof value === "object") {
40
+ if ((key === "padding" || key === "buttonPadding") && typeof value === "object") {
48
41
  const padding = value;
49
42
  value = `${padding.top}px ${padding.right}px ${padding.bottom}px ${padding.left}px`;
50
43
  }
@@ -140,15 +133,11 @@ function convertTextBlock(blockData) {
140
133
  perChanges: [],
141
134
  pxChanges: allPxAttributes,
142
135
  });
143
- const sanitizedText = (props.text ?? "")
144
- .replaceAll(/<p>/g, "<div>")
145
- .replaceAll(/<\/p>/g, "</div>");
136
+ const sanitizedText = (props.text ?? "").replaceAll(/<p>/g, "<div>").replaceAll(/<\/p>/g, "</div>");
146
137
  const navigateToUrl = props.navigateToUrl || "";
147
138
  const convertedTextBox = `<div style="display: inline-block; max-width: 100%; box-sizing: border-box; ${convertedTextStyle}">${sanitizedText.replaceAll(/\n/g, "<br>")}</div>`;
148
139
  const textContent = appendOutlookSupport(convertedTextBox, styles);
149
- return navigateToUrl
150
- ? `<a href="${navigateToUrl}" rel="noreferrer noopener" style="color:inherit; text-decoration:none; cursor:pointer;">${textContent}</a>`
151
- : textContent;
140
+ return navigateToUrl ? `<a href="${navigateToUrl}" rel="noreferrer noopener" style="color:inherit; text-decoration:none; cursor:pointer;">${textContent}</a>` : textContent;
152
141
  }
153
142
  async function appendOutlookForImage(content, outerContainerWidth, innerContainerWidth, imageUrl, style = {}) {
154
143
  const image = await jimp_1.Jimp.read(imageUrl);
@@ -161,12 +150,8 @@ async function appendOutlookForImage(content, outerContainerWidth, innerContaine
161
150
  const borderColor = style?.borderColor || "transparent";
162
151
  const borderRadius = parseInt(style?.borderRadius) || 0;
163
152
  const useRoundRect = borderRadius > 0;
164
- const arcsize = useRoundRect
165
- ? Math.min(borderRadius / scaledHeight, 1).toFixed(2)
166
- : "";
167
- const borderAttributes = borderWidth > 0
168
- ? `strokeweight="${borderWidth}px" strokecolor="${borderColor}"`
169
- : `stroked="false"`;
153
+ const arcsize = useRoundRect ? Math.min(borderRadius / scaledHeight, 1).toFixed(2) : "";
154
+ const borderAttributes = borderWidth > 0 ? `strokeweight="${borderWidth}px" strokecolor="${borderColor}"` : `stroked="false"`;
170
155
  const outlookImage = `<!--[if mso]>
171
156
  <v:${useRoundRect ? "roundrect" : "rect"} xmlns:v="urn:schemas-microsoft-com:vml"
172
157
  style="width:${scaledWidth}px;height:${scaledHeight}px;"
@@ -209,11 +194,7 @@ async function convertImageBlock(blockData, cellWidthInPx) {
209
194
  pxChanges: addPxToAttributes,
210
195
  });
211
196
  const imageElement = `<img src="${imageUrl}" alt="${altText}" style="${imageTagStyles}; max-width: ${originalWidth}px; max-height: ${originalHeight}px;" />`;
212
- const innerContainerWidth = ((typeof width === "string" ? parseInt(width.replace("%", "")) : width) /
213
- 100) *
214
- (cellWidthInPx -
215
- (style?.padding?.left || 0) -
216
- (style?.padding?.right || 0));
197
+ const innerContainerWidth = ((typeof width === "string" ? parseInt(width.replace("%", "")) : width) / 100) * (cellWidthInPx - (style?.padding?.left || 0) - (style?.padding?.right || 0));
217
198
  const outlookImage = await appendOutlookForImage(imageElement, cellWidthInPx, innerContainerWidth, imageUrl, style);
218
199
  const imageContent = appendOutlookSupport(outlookImage, containerStyles);
219
200
  return navigateToUrl
@@ -222,9 +203,7 @@ async function convertImageBlock(blockData, cellWidthInPx) {
222
203
  }
223
204
  function appendOutlookForButton(content, buttonStyle, navigateToUrl, text) {
224
205
  const { 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, } = buttonStyle;
225
- const borderAttributes = borderWidth > 0
226
- ? `strokeweight="${borderWidth}px" strokecolor="${borderColor}"`
227
- : `stroked="false"`;
206
+ const borderAttributes = borderWidth > 0 ? `strokeweight="${borderWidth}px" strokecolor="${borderColor}"` : `stroked="false"`;
228
207
  return `
229
208
  <!--[if mso]>
230
209
  <v:${borderRadius ? "roundrect" : "rect"} xmlns:v="urn:schemas-microsoft-com:vml" href="${navigateToUrl}"
@@ -340,7 +319,7 @@ async function convertGridCellBlock(blockData, rootData, cellWidthPercent, paren
340
319
  }
341
320
  async function convertVideoBlock(blockData, cellWidthInPx) {
342
321
  const { style, props } = blockData.data;
343
- const { videoUrl, youtubeVideoUrl, thumbnailUrl, altText } = props || {};
322
+ const { videoUrl, youtubeVideoUrl, thumbnailUrl, altText } = props;
344
323
  const videoLink = youtubeVideoUrl || videoUrl || "#";
345
324
  let resolvedThumbnail = thumbnailUrl || "https://via.placeholder.com/480x360?text=No+Thumbnail";
346
325
  if (youtubeVideoUrl) {
@@ -453,9 +432,15 @@ async function convertVideoBlock(blockData, cellWidthInPx) {
453
432
  <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin:0; padding:0; border-collapse: collapse;">
454
433
  <tr>
455
434
  <td align="center" style="padding:0; ${outerContainerStyles}">
456
- <div style="display: inline-block; width: ${percentWidth}; max-width: ${cellWidthInPx}px; box-sizing: border-box;">
457
- ${videoContent}
458
- </div>
435
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation"
436
+ align="center"
437
+ style="margin:0 auto; max-width:${cellWidthInPx}px; width:${percentWidth}; border-collapse:collapse;">
438
+ <tr>
439
+ <td align="center" style="text-align:center; padding:0;">
440
+ ${videoContent}
441
+ </td>
442
+ </tr>
443
+ </table>
459
444
  </td>
460
445
  </tr>
461
446
  </table>
@@ -497,8 +482,7 @@ async function appendOutlookForShape(content, outerContainerWidth, innerContaine
497
482
  // pass raw flag so buildVMLShape knows if image already has text baked-in
498
483
  msoHasBakedText: Boolean(opts.msoBakeImageWithText),
499
484
  });
500
- const outlookAlignment = opts.alignment === "center" ? "center" :
501
- opts.alignment === "right" ? "right" : "left";
485
+ const outlookAlignment = opts.alignment === "center" ? "center" : opts.alignment === "right" ? "right" : "left";
502
486
  // Wrap the VML inside a table so Outlook aligns it correctly
503
487
  return `<!--[if mso]>
504
488
  <table align="${outlookAlignment}" border="0" cellpadding="0" cellspacing="0" style="display:inline-block;">
@@ -527,9 +511,7 @@ function buildVMLShape({ shape, widthPx, heightPx, imageUrl, backgroundColor, bo
527
511
  extraAttr = ` arcsize="${computeArcSize(borderRadius, widthPx)}"`;
528
512
  }
529
513
  // image fill (if provided)
530
- const fillMarkup = imageUrl
531
- ? `<v:fill src="${imageUrl}" type="frame" aspect="atleast" />`
532
- : "";
514
+ const fillMarkup = imageUrl ? `<v:fill src="${imageUrl}" type="frame" aspect="atleast" />` : "";
533
515
  // If MSO is given a baked image with text, don't produce a v:textbox overlay text (image already contains text)
534
516
  const includeTextbox = !!text && !msoHasBakedText;
535
517
  // v:textbox: use a table + cell to center the text; avoids many Word quirks
@@ -560,13 +542,13 @@ function buildVMLShape({ shape, widthPx, heightPx, imageUrl, backgroundColor, bo
560
542
  // ---------- convertShapeBlock (updated, keeps your structure) ----------
561
543
  async function convertShapeBlock(blockData) {
562
544
  const { style, props } = blockData.data;
563
- const { shape, text, textColor = "#000000", imageUrl } = props || {};
545
+ const { shape, text, textColor = "#000000", imageUrl } = props;
564
546
  const { width = "100", height = "150", padding = {}, backgroundColor = "#2F80ED", borderRadius, borderWidth = 0, borderStyle = "solid", borderColor = "transparent", customCss, shapeColor, alignment = "left", msoBakeImageWithText } = style || {};
565
547
  const borderRadiusMap = {
566
548
  rectangle: "0",
567
549
  rounded: "10px",
568
550
  circle: "50%",
569
- oval: "50%",
551
+ oval: "50%", // Keep this for modern browsers
570
552
  };
571
553
  let resolvedBorderRadius = borderRadius || borderRadiusMap[shape] || "0";
572
554
  let resolvedWidthPx = typeof width === "number"
@@ -575,13 +557,16 @@ async function convertShapeBlock(blockData) {
575
557
  let resolvedHeightPx = typeof height === "number"
576
558
  ? height
577
559
  : parseInt(height.toString().replace("px", ""), 10) || 150;
578
- // Force circle square
560
+ // Special handling for different shapes
579
561
  if (shape === "circle") {
562
+ // Circle: make it a perfect square with 50% border radius
580
563
  const side = Math.min(resolvedWidthPx, resolvedHeightPx);
581
564
  resolvedWidthPx = side;
582
565
  resolvedHeightPx = side;
583
566
  resolvedBorderRadius = "50%";
584
567
  }
568
+ else if (shape === "oval") {
569
+ }
585
570
  const finalWidthPx = resolvedWidthPx;
586
571
  const finalHeightPx = resolvedHeightPx;
587
572
  const alignmentStyles = {
@@ -593,12 +578,14 @@ async function convertShapeBlock(blockData) {
593
578
  const finalBackgroundColor = shapeColor || backgroundColor;
594
579
  // --- Modern clients content ---
595
580
  let nonMsoContent = "";
581
+ // For modern browsers, use CSS border-radius
582
+ const modernBorderRadius = shape === "oval" ? "50%" : resolvedBorderRadius;
596
583
  // Case 1: Image + Text → use background-image
597
584
  if (imageUrl && text) {
598
585
  nonMsoContent = `
599
586
  <div style="display:inline-block;width:${finalWidthPx}px;height:${finalHeightPx}px;
600
587
  border:${borderWidth}px ${borderStyle} ${borderColor};
601
- border-radius:${resolvedBorderRadius};
588
+ border-radius:${modernBorderRadius};
602
589
  background:${finalBackgroundColor} url('${imageUrl}') center/cover no-repeat;
603
590
  overflow:hidden;${alignmentStyle}${customCss || ""}">
604
591
  <div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;">
@@ -614,11 +601,11 @@ async function convertShapeBlock(blockData) {
614
601
  nonMsoContent = `
615
602
  <div style="display:inline-block;width:${finalWidthPx}px;height:${finalHeightPx}px;
616
603
  border:${borderWidth}px ${borderStyle} ${borderColor};
617
- border-radius:${resolvedBorderRadius};
604
+ border-radius:${modernBorderRadius};
618
605
  overflow:hidden;${alignmentStyle}${customCss || ""}">
619
606
  <img src="${imageUrl}" alt="${text || "Shape image"}"
620
607
  width="${finalWidthPx}" height="${finalHeightPx}"
621
- style="width:100%;height:100%;object-fit:cover;border-radius:${resolvedBorderRadius};display:block;" />
608
+ style="width:100%;height:100%;object-fit:cover;border-radius:${modernBorderRadius};display:block;" />
622
609
  </div>`;
623
610
  }
624
611
  // Case 3: No image → solid background
@@ -627,7 +614,7 @@ async function convertShapeBlock(blockData) {
627
614
  <div style="display:inline-block;width:${finalWidthPx}px;height:${finalHeightPx}px;
628
615
  background:${finalBackgroundColor};
629
616
  border:${borderWidth}px ${borderStyle} ${borderColor};
630
- border-radius:${resolvedBorderRadius};
617
+ border-radius:${modernBorderRadius};
631
618
  ${alignmentStyle}${customCss || ""}">
632
619
  <div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;
633
620
  color:${textColor};text-align:center;padding:8px;box-sizing:border-box;word-break:break-word;">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "email-builder-utils",
3
- "version": "1.1.22",
3
+ "version": "1.1.24",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [