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/CHANGELOG.md +103 -0
- package/PREVIEW_CUSTOMIZATION_TASK.md +201 -0
- package/README.md +97 -0
- package/dist/collections.cjs +177 -20
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +177 -20
- package/dist/collections.js.map +1 -1
- package/dist/components.cjs +314 -143
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +2 -0
- package/dist/components.d.ts +2 -0
- package/dist/components.js +251 -80
- package/dist/components.js.map +1 -1
- package/dist/index.cjs +201 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +198 -20
- package/dist/index.js.map +1 -1
- package/dist/types.d.cts +23 -0
- package/dist/types.d.ts +23 -0
- package/dist/utils.cjs +172 -18
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +5 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +172 -18
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/collections.js
CHANGED
|
@@ -963,6 +963,12 @@ async function convertToEmailSafeHtml(editorState, options) {
|
|
|
963
963
|
const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl, options?.customBlockConverter);
|
|
964
964
|
const sanitizedHtml = DOMPurify.sanitize(rawHtml, EMAIL_SAFE_CONFIG);
|
|
965
965
|
if (options?.wrapInTemplate) {
|
|
966
|
+
if (options.customWrapper) {
|
|
967
|
+
return await Promise.resolve(options.customWrapper(sanitizedHtml, {
|
|
968
|
+
preheader: options.preheader,
|
|
969
|
+
subject: options.subject
|
|
970
|
+
}));
|
|
971
|
+
}
|
|
966
972
|
return wrapInEmailTemplate(sanitizedHtml, options.preheader);
|
|
967
973
|
}
|
|
968
974
|
return sanitizedHtml;
|
|
@@ -1016,9 +1022,9 @@ async function convertParagraph(node, mediaUrl, customBlockConverter) {
|
|
|
1016
1022
|
);
|
|
1017
1023
|
const children = childParts.join("");
|
|
1018
1024
|
if (!children.trim()) {
|
|
1019
|
-
return '<p style="margin: 0 0 16px 0; min-height: 1em;"> </p>';
|
|
1025
|
+
return '<p class="mobile-margin-bottom-16" style="margin: 0 0 16px 0; min-height: 1em;"> </p>';
|
|
1020
1026
|
}
|
|
1021
|
-
return `<p style="margin: 0 0 16px 0; text-align: ${align};">${children}</p>`;
|
|
1027
|
+
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>`;
|
|
1022
1028
|
}
|
|
1023
1029
|
async function convertHeading(node, mediaUrl, customBlockConverter) {
|
|
1024
1030
|
const tag = node.tag || "h1";
|
|
@@ -1032,8 +1038,14 @@ async function convertHeading(node, mediaUrl, customBlockConverter) {
|
|
|
1032
1038
|
h2: "font-size: 24px; font-weight: 600; margin: 0 0 16px 0; line-height: 1.3;",
|
|
1033
1039
|
h3: "font-size: 20px; font-weight: 600; margin: 0 0 12px 0; line-height: 1.4;"
|
|
1034
1040
|
};
|
|
1041
|
+
const mobileClasses = {
|
|
1042
|
+
h1: "mobile-font-size-24",
|
|
1043
|
+
h2: "mobile-font-size-20",
|
|
1044
|
+
h3: "mobile-font-size-16"
|
|
1045
|
+
};
|
|
1035
1046
|
const style = `${styles2[tag] || styles2.h3} text-align: ${align};`;
|
|
1036
|
-
|
|
1047
|
+
const mobileClass = mobileClasses[tag] || mobileClasses.h3;
|
|
1048
|
+
return `<${tag} class="${mobileClass}" style="${style}">${children}</${tag}>`;
|
|
1037
1049
|
}
|
|
1038
1050
|
async function convertList(node, mediaUrl, customBlockConverter) {
|
|
1039
1051
|
const tag = node.listType === "number" ? "ol" : "ul";
|
|
@@ -1041,8 +1053,8 @@ async function convertList(node, mediaUrl, customBlockConverter) {
|
|
|
1041
1053
|
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1042
1054
|
);
|
|
1043
1055
|
const children = childParts.join("");
|
|
1044
|
-
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;";
|
|
1045
|
-
return `<${tag} style="${style}">${children}</${tag}>`;
|
|
1056
|
+
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;";
|
|
1057
|
+
return `<${tag} class="mobile-margin-bottom-16" style="${style}">${children}</${tag}>`;
|
|
1046
1058
|
}
|
|
1047
1059
|
async function convertListItem(node, mediaUrl, customBlockConverter) {
|
|
1048
1060
|
const childParts = await Promise.all(
|
|
@@ -1099,16 +1111,16 @@ function convertUpload(node, mediaUrl) {
|
|
|
1099
1111
|
}
|
|
1100
1112
|
const alt = node.fields?.altText || upload.alt || "";
|
|
1101
1113
|
const caption = node.fields?.caption || "";
|
|
1102
|
-
const imgHtml = `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}" style="max-width: 100%; height: auto; display: block; margin: 0 auto;" />`;
|
|
1114
|
+
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;" />`;
|
|
1103
1115
|
if (caption) {
|
|
1104
1116
|
return `
|
|
1105
|
-
<div style="margin: 0 0 16px 0; text-align: center;">
|
|
1117
|
+
<div style="margin: 0 0 16px 0; text-align: center;" class="mobile-margin-bottom-16">
|
|
1106
1118
|
${imgHtml}
|
|
1107
|
-
<p style="margin: 8px 0 0 0; font-size: 14px; color: #6b7280; font-style: italic;">${escapeHtml(caption)}</p>
|
|
1119
|
+
<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>
|
|
1108
1120
|
</div>
|
|
1109
1121
|
`;
|
|
1110
1122
|
}
|
|
1111
|
-
return `<div style="margin: 0 0 16px 0; text-align: center;">${imgHtml}</div>`;
|
|
1123
|
+
return `<div style="margin: 0 0 16px 0; text-align: center;" class="mobile-margin-bottom-16">${imgHtml}</div>`;
|
|
1112
1124
|
}
|
|
1113
1125
|
async function convertBlock(node, mediaUrl, customBlockConverter) {
|
|
1114
1126
|
const blockType = node.fields?.blockName || node.blockName;
|
|
@@ -1181,11 +1193,14 @@ function escapeHtml(text) {
|
|
|
1181
1193
|
}
|
|
1182
1194
|
function wrapInEmailTemplate(content, preheader) {
|
|
1183
1195
|
return `<!DOCTYPE html>
|
|
1184
|
-
<html lang="en">
|
|
1196
|
+
<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">
|
|
1185
1197
|
<head>
|
|
1186
1198
|
<meta charset="UTF-8">
|
|
1187
1199
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1188
|
-
<
|
|
1200
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
1201
|
+
<meta name="x-apple-disable-message-reformatting">
|
|
1202
|
+
<title>Newsletter</title>
|
|
1203
|
+
|
|
1189
1204
|
<!--[if mso]>
|
|
1190
1205
|
<noscript>
|
|
1191
1206
|
<xml>
|
|
@@ -1195,16 +1210,155 @@ function wrapInEmailTemplate(content, preheader) {
|
|
|
1195
1210
|
</xml>
|
|
1196
1211
|
</noscript>
|
|
1197
1212
|
<![endif]-->
|
|
1213
|
+
|
|
1214
|
+
<style>
|
|
1215
|
+
/* Reset and base styles */
|
|
1216
|
+
* {
|
|
1217
|
+
-webkit-text-size-adjust: 100%;
|
|
1218
|
+
-ms-text-size-adjust: 100%;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
body {
|
|
1222
|
+
margin: 0 !important;
|
|
1223
|
+
padding: 0 !important;
|
|
1224
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
|
|
1225
|
+
font-size: 16px;
|
|
1226
|
+
line-height: 1.5;
|
|
1227
|
+
color: #1A1A1A;
|
|
1228
|
+
background-color: #f8f9fa;
|
|
1229
|
+
-webkit-font-smoothing: antialiased;
|
|
1230
|
+
-moz-osx-font-smoothing: grayscale;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
table {
|
|
1234
|
+
border-spacing: 0 !important;
|
|
1235
|
+
border-collapse: collapse !important;
|
|
1236
|
+
table-layout: fixed !important;
|
|
1237
|
+
margin: 0 auto !important;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
table table table {
|
|
1241
|
+
table-layout: auto;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
img {
|
|
1245
|
+
-ms-interpolation-mode: bicubic;
|
|
1246
|
+
max-width: 100%;
|
|
1247
|
+
height: auto;
|
|
1248
|
+
border: 0;
|
|
1249
|
+
outline: none;
|
|
1250
|
+
text-decoration: none;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/* Responsive styles */
|
|
1254
|
+
@media only screen and (max-width: 640px) {
|
|
1255
|
+
.mobile-hide {
|
|
1256
|
+
display: none !important;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
.mobile-center {
|
|
1260
|
+
text-align: center !important;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
.mobile-width-100 {
|
|
1264
|
+
width: 100% !important;
|
|
1265
|
+
max-width: 100% !important;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
.mobile-padding {
|
|
1269
|
+
padding: 20px !important;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
.mobile-padding-sm {
|
|
1273
|
+
padding: 16px !important;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
.mobile-font-size-14 {
|
|
1277
|
+
font-size: 14px !important;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
.mobile-font-size-16 {
|
|
1281
|
+
font-size: 16px !important;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
.mobile-font-size-20 {
|
|
1285
|
+
font-size: 20px !important;
|
|
1286
|
+
line-height: 1.3 !important;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
.mobile-font-size-24 {
|
|
1290
|
+
font-size: 24px !important;
|
|
1291
|
+
line-height: 1.2 !important;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/* Stack sections on mobile */
|
|
1295
|
+
.mobile-stack {
|
|
1296
|
+
display: block !important;
|
|
1297
|
+
width: 100% !important;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/* Mobile-specific spacing */
|
|
1301
|
+
.mobile-margin-bottom-16 {
|
|
1302
|
+
margin-bottom: 16px !important;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
.mobile-margin-bottom-20 {
|
|
1306
|
+
margin-bottom: 20px !important;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/* Dark mode support */
|
|
1311
|
+
@media (prefers-color-scheme: dark) {
|
|
1312
|
+
.dark-mode-bg {
|
|
1313
|
+
background-color: #1a1a1a !important;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
.dark-mode-text {
|
|
1317
|
+
color: #ffffff !important;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
.dark-mode-border {
|
|
1321
|
+
border-color: #333333 !important;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/* Outlook-specific fixes */
|
|
1326
|
+
<!--[if mso]>
|
|
1327
|
+
<style>
|
|
1328
|
+
table {
|
|
1329
|
+
border-collapse: collapse;
|
|
1330
|
+
border-spacing: 0;
|
|
1331
|
+
border: none;
|
|
1332
|
+
margin: 0;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
div, p {
|
|
1336
|
+
margin: 0;
|
|
1337
|
+
}
|
|
1338
|
+
</style>
|
|
1339
|
+
<![endif]-->
|
|
1340
|
+
</style>
|
|
1198
1341
|
</head>
|
|
1199
|
-
<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: #
|
|
1200
|
-
${preheader ?
|
|
1201
|
-
|
|
1342
|
+
<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;">
|
|
1343
|
+
${preheader ? `
|
|
1344
|
+
<!-- Preheader text -->
|
|
1345
|
+
<div style="display: none; max-height: 0; overflow: hidden; font-size: 1px; line-height: 1px; color: transparent;">
|
|
1346
|
+
${escapeHtml(preheader)}
|
|
1347
|
+
</div>
|
|
1348
|
+
` : ""}
|
|
1349
|
+
|
|
1350
|
+
<!-- Main container -->
|
|
1351
|
+
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin: 0; padding: 0; background-color: #f8f9fa;">
|
|
1202
1352
|
<tr>
|
|
1203
|
-
<td align="center" style="padding: 20px
|
|
1204
|
-
|
|
1353
|
+
<td align="center" style="padding: 20px 10px;">
|
|
1354
|
+
<!-- Email wrapper -->
|
|
1355
|
+
<table role="presentation" cellpadding="0" cellspacing="0" width="600" class="mobile-width-100" style="margin: 0 auto; max-width: 600px;">
|
|
1205
1356
|
<tr>
|
|
1206
|
-
<td style="padding:
|
|
1207
|
-
|
|
1357
|
+
<td class="mobile-padding" style="padding: 0;">
|
|
1358
|
+
<!-- Content area with light background -->
|
|
1359
|
+
<div style="background-color: #ffffff; padding: 40px 30px; border-radius: 8px;" class="mobile-padding">
|
|
1360
|
+
${content}
|
|
1361
|
+
</div>
|
|
1208
1362
|
</td>
|
|
1209
1363
|
</tr>
|
|
1210
1364
|
</table>
|
|
@@ -1709,11 +1863,14 @@ var createBroadcastPreviewEndpoint = (config, _collectionSlug) => {
|
|
|
1709
1863
|
const mediaUrl = req.payload.config.serverURL ? `${req.payload.config.serverURL}/api/media` : "/api/media";
|
|
1710
1864
|
req.payload.logger?.info("Populating media fields for email preview...");
|
|
1711
1865
|
const populatedContent = await populateMediaFields(content, req.payload, config);
|
|
1866
|
+
const emailPreviewConfig = config.customizations?.broadcasts?.emailPreview;
|
|
1712
1867
|
const htmlContent = await convertToEmailSafeHtml(populatedContent, {
|
|
1713
|
-
wrapInTemplate: true,
|
|
1868
|
+
wrapInTemplate: emailPreviewConfig?.wrapInTemplate ?? true,
|
|
1714
1869
|
preheader,
|
|
1870
|
+
subject,
|
|
1715
1871
|
mediaUrl,
|
|
1716
|
-
customBlockConverter: config.customizations?.broadcasts?.customBlockConverter
|
|
1872
|
+
customBlockConverter: config.customizations?.broadcasts?.customBlockConverter,
|
|
1873
|
+
customWrapper: emailPreviewConfig?.customWrapper
|
|
1717
1874
|
});
|
|
1718
1875
|
return Response.json({
|
|
1719
1876
|
success: true,
|