payload-plugin-newsletter 0.18.0 → 0.20.0

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/index.cjs CHANGED
@@ -559,12 +559,15 @@ var init_broadcast2 = __esm({
559
559
  // src/index.ts
560
560
  var src_exports = {};
561
561
  __export(src_exports, {
562
+ PluginConfigProvider: () => PluginConfigProvider,
562
563
  default: () => newsletterPlugin,
563
564
  getServerSideAuth: () => getServerSideAuth,
564
565
  getTokenFromRequest: () => getTokenFromRequest,
565
566
  isAuthenticated: () => isAuthenticated,
566
567
  newsletterPlugin: () => newsletterPlugin,
567
568
  requireAuth: () => requireAuth,
569
+ usePluginConfig: () => usePluginConfig,
570
+ usePluginConfigOptional: () => usePluginConfigOptional,
568
571
  verifyToken: () => verifyToken
569
572
  });
570
573
  module.exports = __toCommonJS(src_exports);
@@ -3584,6 +3587,12 @@ async function convertToEmailSafeHtml(editorState, options) {
3584
3587
  const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl, options?.customBlockConverter);
3585
3588
  const sanitizedHtml = import_isomorphic_dompurify2.default.sanitize(rawHtml, EMAIL_SAFE_CONFIG);
3586
3589
  if (options?.wrapInTemplate) {
3590
+ if (options.customWrapper) {
3591
+ return await Promise.resolve(options.customWrapper(sanitizedHtml, {
3592
+ preheader: options.preheader,
3593
+ subject: options.subject
3594
+ }));
3595
+ }
3587
3596
  return wrapInEmailTemplate(sanitizedHtml, options.preheader);
3588
3597
  }
3589
3598
  return sanitizedHtml;
@@ -3637,9 +3646,9 @@ async function convertParagraph(node, mediaUrl, customBlockConverter) {
3637
3646
  );
3638
3647
  const children = childParts.join("");
3639
3648
  if (!children.trim()) {
3640
- return '<p style="margin: 0 0 16px 0; min-height: 1em;">&nbsp;</p>';
3649
+ return '<p class="mobile-margin-bottom-16" style="margin: 0 0 16px 0; min-height: 1em;">&nbsp;</p>';
3641
3650
  }
3642
- return `<p style="margin: 0 0 16px 0; text-align: ${align};">${children}</p>`;
3651
+ return `<p class="mobile-margin-bottom-16" style="margin: 0 0 16px 0; text-align: ${align}; font-size: 16px; line-height: 1.5;">${children}</p>`;
3643
3652
  }
3644
3653
  async function convertHeading(node, mediaUrl, customBlockConverter) {
3645
3654
  const tag = node.tag || "h1";
@@ -3653,8 +3662,14 @@ async function convertHeading(node, mediaUrl, customBlockConverter) {
3653
3662
  h2: "font-size: 24px; font-weight: 600; margin: 0 0 16px 0; line-height: 1.3;",
3654
3663
  h3: "font-size: 20px; font-weight: 600; margin: 0 0 12px 0; line-height: 1.4;"
3655
3664
  };
3665
+ const mobileClasses = {
3666
+ h1: "mobile-font-size-24",
3667
+ h2: "mobile-font-size-20",
3668
+ h3: "mobile-font-size-16"
3669
+ };
3656
3670
  const style = `${styles2[tag] || styles2.h3} text-align: ${align};`;
3657
- return `<${tag} style="${style}">${children}</${tag}>`;
3671
+ const mobileClass = mobileClasses[tag] || mobileClasses.h3;
3672
+ return `<${tag} class="${mobileClass}" style="${style}">${children}</${tag}>`;
3658
3673
  }
3659
3674
  async function convertList(node, mediaUrl, customBlockConverter) {
3660
3675
  const tag = node.listType === "number" ? "ol" : "ul";
@@ -3662,8 +3677,8 @@ async function convertList(node, mediaUrl, customBlockConverter) {
3662
3677
  (node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
3663
3678
  );
3664
3679
  const children = childParts.join("");
3665
- const style = tag === "ul" ? "margin: 0 0 16px 0; padding-left: 24px; list-style-type: disc;" : "margin: 0 0 16px 0; padding-left: 24px; list-style-type: decimal;";
3666
- return `<${tag} style="${style}">${children}</${tag}>`;
3680
+ const style = tag === "ul" ? "margin: 0 0 16px 0; padding-left: 24px; list-style-type: disc; font-size: 16px; line-height: 1.5;" : "margin: 0 0 16px 0; padding-left: 24px; list-style-type: decimal; font-size: 16px; line-height: 1.5;";
3681
+ return `<${tag} class="mobile-margin-bottom-16" style="${style}">${children}</${tag}>`;
3667
3682
  }
3668
3683
  async function convertListItem(node, mediaUrl, customBlockConverter) {
3669
3684
  const childParts = await Promise.all(
@@ -3720,16 +3735,16 @@ function convertUpload(node, mediaUrl) {
3720
3735
  }
3721
3736
  const alt = node.fields?.altText || upload.alt || "";
3722
3737
  const caption = node.fields?.caption || "";
3723
- const imgHtml = `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}" style="max-width: 100%; height: auto; display: block; margin: 0 auto;" />`;
3738
+ const imgHtml = `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}" class="mobile-width-100" style="max-width: 100%; height: auto; display: block; margin: 0 auto; border-radius: 6px;" />`;
3724
3739
  if (caption) {
3725
3740
  return `
3726
- <div style="margin: 0 0 16px 0; text-align: center;">
3741
+ <div style="margin: 0 0 16px 0; text-align: center;" class="mobile-margin-bottom-16">
3727
3742
  ${imgHtml}
3728
- <p style="margin: 8px 0 0 0; font-size: 14px; color: #6b7280; font-style: italic;">${escapeHtml(caption)}</p>
3743
+ <p style="margin: 8px 0 0 0; font-size: 14px; color: #6b7280; font-style: italic; text-align: center;" class="mobile-font-size-14">${escapeHtml(caption)}</p>
3729
3744
  </div>
3730
3745
  `;
3731
3746
  }
3732
- return `<div style="margin: 0 0 16px 0; text-align: center;">${imgHtml}</div>`;
3747
+ return `<div style="margin: 0 0 16px 0; text-align: center;" class="mobile-margin-bottom-16">${imgHtml}</div>`;
3733
3748
  }
3734
3749
  async function convertBlock(node, mediaUrl, customBlockConverter) {
3735
3750
  const blockType = node.fields?.blockName || node.blockName;
@@ -3802,11 +3817,14 @@ function escapeHtml(text) {
3802
3817
  }
3803
3818
  function wrapInEmailTemplate(content, preheader) {
3804
3819
  return `<!DOCTYPE html>
3805
- <html lang="en">
3820
+ <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
3806
3821
  <head>
3807
3822
  <meta charset="UTF-8">
3808
3823
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
3809
- <title>Email</title>
3824
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
3825
+ <meta name="x-apple-disable-message-reformatting">
3826
+ <title>Newsletter</title>
3827
+
3810
3828
  <!--[if mso]>
3811
3829
  <noscript>
3812
3830
  <xml>
@@ -3816,16 +3834,155 @@ function wrapInEmailTemplate(content, preheader) {
3816
3834
  </xml>
3817
3835
  </noscript>
3818
3836
  <![endif]-->
3837
+
3838
+ <style>
3839
+ /* Reset and base styles */
3840
+ * {
3841
+ -webkit-text-size-adjust: 100%;
3842
+ -ms-text-size-adjust: 100%;
3843
+ }
3844
+
3845
+ body {
3846
+ margin: 0 !important;
3847
+ padding: 0 !important;
3848
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
3849
+ font-size: 16px;
3850
+ line-height: 1.5;
3851
+ color: #1A1A1A;
3852
+ background-color: #f8f9fa;
3853
+ -webkit-font-smoothing: antialiased;
3854
+ -moz-osx-font-smoothing: grayscale;
3855
+ }
3856
+
3857
+ table {
3858
+ border-spacing: 0 !important;
3859
+ border-collapse: collapse !important;
3860
+ table-layout: fixed !important;
3861
+ margin: 0 auto !important;
3862
+ }
3863
+
3864
+ table table table {
3865
+ table-layout: auto;
3866
+ }
3867
+
3868
+ img {
3869
+ -ms-interpolation-mode: bicubic;
3870
+ max-width: 100%;
3871
+ height: auto;
3872
+ border: 0;
3873
+ outline: none;
3874
+ text-decoration: none;
3875
+ }
3876
+
3877
+ /* Responsive styles */
3878
+ @media only screen and (max-width: 640px) {
3879
+ .mobile-hide {
3880
+ display: none !important;
3881
+ }
3882
+
3883
+ .mobile-center {
3884
+ text-align: center !important;
3885
+ }
3886
+
3887
+ .mobile-width-100 {
3888
+ width: 100% !important;
3889
+ max-width: 100% !important;
3890
+ }
3891
+
3892
+ .mobile-padding {
3893
+ padding: 20px !important;
3894
+ }
3895
+
3896
+ .mobile-padding-sm {
3897
+ padding: 16px !important;
3898
+ }
3899
+
3900
+ .mobile-font-size-14 {
3901
+ font-size: 14px !important;
3902
+ }
3903
+
3904
+ .mobile-font-size-16 {
3905
+ font-size: 16px !important;
3906
+ }
3907
+
3908
+ .mobile-font-size-20 {
3909
+ font-size: 20px !important;
3910
+ line-height: 1.3 !important;
3911
+ }
3912
+
3913
+ .mobile-font-size-24 {
3914
+ font-size: 24px !important;
3915
+ line-height: 1.2 !important;
3916
+ }
3917
+
3918
+ /* Stack sections on mobile */
3919
+ .mobile-stack {
3920
+ display: block !important;
3921
+ width: 100% !important;
3922
+ }
3923
+
3924
+ /* Mobile-specific spacing */
3925
+ .mobile-margin-bottom-16 {
3926
+ margin-bottom: 16px !important;
3927
+ }
3928
+
3929
+ .mobile-margin-bottom-20 {
3930
+ margin-bottom: 20px !important;
3931
+ }
3932
+ }
3933
+
3934
+ /* Dark mode support */
3935
+ @media (prefers-color-scheme: dark) {
3936
+ .dark-mode-bg {
3937
+ background-color: #1a1a1a !important;
3938
+ }
3939
+
3940
+ .dark-mode-text {
3941
+ color: #ffffff !important;
3942
+ }
3943
+
3944
+ .dark-mode-border {
3945
+ border-color: #333333 !important;
3946
+ }
3947
+ }
3948
+
3949
+ /* Outlook-specific fixes */
3950
+ <!--[if mso]>
3951
+ <style>
3952
+ table {
3953
+ border-collapse: collapse;
3954
+ border-spacing: 0;
3955
+ border: none;
3956
+ margin: 0;
3957
+ }
3958
+
3959
+ div, p {
3960
+ margin: 0;
3961
+ }
3962
+ </style>
3963
+ <![endif]-->
3964
+ </style>
3819
3965
  </head>
3820
- <body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 16px; line-height: 1.5; color: #333333; background-color: #f3f4f6;">
3821
- ${preheader ? `<div style="display: none; max-height: 0; overflow: hidden;">${escapeHtml(preheader)}</div>` : ""}
3822
- <table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin: 0; padding: 0;">
3966
+ <body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 16px; line-height: 1.5; color: #1A1A1A; background-color: #f8f9fa;">
3967
+ ${preheader ? `
3968
+ <!-- Preheader text -->
3969
+ <div style="display: none; max-height: 0; overflow: hidden; font-size: 1px; line-height: 1px; color: transparent;">
3970
+ ${escapeHtml(preheader)}
3971
+ </div>
3972
+ ` : ""}
3973
+
3974
+ <!-- Main container -->
3975
+ <table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin: 0; padding: 0; background-color: #f8f9fa;">
3823
3976
  <tr>
3824
- <td align="center" style="padding: 20px 0;">
3825
- <table role="presentation" cellpadding="0" cellspacing="0" width="600" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; overflow: hidden;">
3977
+ <td align="center" style="padding: 20px 10px;">
3978
+ <!-- Email wrapper -->
3979
+ <table role="presentation" cellpadding="0" cellspacing="0" width="600" class="mobile-width-100" style="margin: 0 auto; max-width: 600px;">
3826
3980
  <tr>
3827
- <td style="padding: 40px 30px;">
3828
- ${content}
3981
+ <td class="mobile-padding" style="padding: 0;">
3982
+ <!-- Content area with light background -->
3983
+ <div style="background-color: #ffffff; padding: 40px 30px; border-radius: 8px;" class="mobile-padding">
3984
+ ${content}
3985
+ </div>
3829
3986
  </td>
3830
3987
  </tr>
3831
3988
  </table>
@@ -4269,11 +4426,14 @@ var createBroadcastPreviewEndpoint = (config, _collectionSlug) => {
4269
4426
  const mediaUrl = req.payload.config.serverURL ? `${req.payload.config.serverURL}/api/media` : "/api/media";
4270
4427
  req.payload.logger?.info("Populating media fields for email preview...");
4271
4428
  const populatedContent = await populateMediaFields(content, req.payload, config);
4429
+ const emailPreviewConfig = config.customizations?.broadcasts?.emailPreview;
4272
4430
  const htmlContent = await convertToEmailSafeHtml(populatedContent, {
4273
- wrapInTemplate: true,
4431
+ wrapInTemplate: emailPreviewConfig?.wrapInTemplate ?? true,
4274
4432
  preheader,
4433
+ subject,
4275
4434
  mediaUrl,
4276
- customBlockConverter: config.customizations?.broadcasts?.customBlockConverter
4435
+ customBlockConverter: config.customizations?.broadcasts?.customBlockConverter,
4436
+ customWrapper: emailPreviewConfig?.customWrapper
4277
4437
  });
4278
4438
  return Response.json({
4279
4439
  success: true,
@@ -5177,6 +5337,24 @@ var isAuthenticated = (req, secret) => {
5177
5337
  return !!decoded;
5178
5338
  };
5179
5339
 
5340
+ // src/contexts/PluginConfigContext.tsx
5341
+ var import_react = require("react");
5342
+ var import_jsx_runtime5 = require("react/jsx-runtime");
5343
+ var PluginConfigContext = (0, import_react.createContext)(null);
5344
+ var PluginConfigProvider = ({ config, children }) => {
5345
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PluginConfigContext.Provider, { value: config, children });
5346
+ };
5347
+ var usePluginConfig = () => {
5348
+ const config = (0, import_react.useContext)(PluginConfigContext);
5349
+ if (!config) {
5350
+ throw new Error("usePluginConfig must be used within PluginConfigProvider");
5351
+ }
5352
+ return config;
5353
+ };
5354
+ var usePluginConfigOptional = () => {
5355
+ return (0, import_react.useContext)(PluginConfigContext);
5356
+ };
5357
+
5180
5358
  // src/index.ts
5181
5359
  var newsletterPlugin = (pluginConfig) => (incomingConfig) => {
5182
5360
  const config = {
@@ -5354,11 +5532,14 @@ var newsletterPlugin = (pluginConfig) => (incomingConfig) => {
5354
5532
  };
5355
5533
  // Annotate the CommonJS export names for ESM import in node:
5356
5534
  0 && (module.exports = {
5535
+ PluginConfigProvider,
5357
5536
  getServerSideAuth,
5358
5537
  getTokenFromRequest,
5359
5538
  isAuthenticated,
5360
5539
  newsletterPlugin,
5361
5540
  requireAuth,
5541
+ usePluginConfig,
5542
+ usePluginConfigOptional,
5362
5543
  verifyToken
5363
5544
  });
5364
5545
  //# sourceMappingURL=index.cjs.map