payload-plugin-newsletter 0.15.0 → 0.15.1
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 +28 -0
- package/README.md +4 -2
- package/dist/collections.cjs +183 -11
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +183 -11
- package/dist/collections.js.map +1 -1
- package/dist/fields.cjs +182 -6
- package/dist/fields.cjs.map +1 -1
- package/dist/fields.d.cts +16 -1
- package/dist/fields.d.ts +16 -1
- package/dist/fields.js +178 -5
- package/dist/fields.js.map +1 -1
- package/dist/index.cjs +183 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +183 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
## [0.15.1] - 2025-07-27
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
- **Email-Compatible Block Editor** - Resolved Next.js serialization errors with custom blocks
|
|
5
|
+
- Custom blocks are now processed server-side using Lexical's proven BlocksFeature pattern
|
|
6
|
+
- Prevents "Functions cannot be passed directly to Client Components" errors
|
|
7
|
+
- Maintains full email compatibility while enabling custom block functionality
|
|
8
|
+
- **Block Validation System** - Added validation utilities for email compatibility
|
|
9
|
+
- `validateEmailBlocks()` warns about potentially incompatible block types
|
|
10
|
+
- `createEmailSafeBlocks()` processes blocks for email-safe configurations
|
|
11
|
+
- Automatic detection of complex field types that may not render in email clients
|
|
12
|
+
|
|
13
|
+
### Improved
|
|
14
|
+
- **Server-Side Block Processing** - Enhanced `createEmailLexicalEditor()` function
|
|
15
|
+
- Processes custom blocks into Lexical editor configuration before client serialization
|
|
16
|
+
- Clean separation between email-compatible and web-only content blocks
|
|
17
|
+
- Better performance through pre-configured editor instances
|
|
18
|
+
- **Enhanced Documentation** - Updated extension points guide with new approach
|
|
19
|
+
- Examples showing both legacy and new server-side processing methods
|
|
20
|
+
- Block validation utilities documentation
|
|
21
|
+
- Email compatibility best practices
|
|
22
|
+
|
|
23
|
+
### Technical
|
|
24
|
+
- Added `createEmailLexicalEditor()` for server-side editor configuration
|
|
25
|
+
- Enhanced `createEmailContentField()` to accept pre-configured editors
|
|
26
|
+
- New utility exports: `validateEmailBlocks`, `createEmailSafeBlocks`
|
|
27
|
+
- Improved TypeScript support for custom block configurations
|
|
28
|
+
|
|
1
29
|
## [0.15.0] - 2025-07-27
|
|
2
30
|
|
|
3
31
|
### Added
|
package/README.md
CHANGED
|
@@ -727,7 +727,7 @@ newsletterPlugin({
|
|
|
727
727
|
|
|
728
728
|
### Extending the Broadcasts Collection (v0.15.0+)
|
|
729
729
|
|
|
730
|
-
You can extend the Broadcasts collection with additional fields and custom blocks:
|
|
730
|
+
You can extend the Broadcasts collection with additional fields and custom email-compatible blocks:
|
|
731
731
|
|
|
732
732
|
```typescript
|
|
733
733
|
import type { Block } from 'payload'
|
|
@@ -753,7 +753,7 @@ newsletterPlugin({
|
|
|
753
753
|
admin: { position: 'sidebar' }
|
|
754
754
|
}
|
|
755
755
|
],
|
|
756
|
-
customBlocks: [customBlock],
|
|
756
|
+
customBlocks: [customBlock], // Processed server-side for email compatibility
|
|
757
757
|
fieldOverrides: {
|
|
758
758
|
content: (defaultField) => ({
|
|
759
759
|
...defaultField,
|
|
@@ -768,6 +768,8 @@ newsletterPlugin({
|
|
|
768
768
|
})
|
|
769
769
|
```
|
|
770
770
|
|
|
771
|
+
**Note**: Custom blocks are processed server-side to ensure email compatibility and prevent Next.js serialization errors.
|
|
772
|
+
|
|
771
773
|
For complete extensibility documentation, see the [Extension Points Guide](./docs/architecture/extension-points.md).
|
|
772
774
|
|
|
773
775
|
## Troubleshooting
|
package/dist/collections.cjs
CHANGED
|
@@ -542,6 +542,103 @@ init_types();
|
|
|
542
542
|
|
|
543
543
|
// src/fields/emailContent.ts
|
|
544
544
|
var import_richtext_lexical = require("@payloadcms/richtext-lexical");
|
|
545
|
+
|
|
546
|
+
// src/utils/blockValidation.ts
|
|
547
|
+
var EMAIL_INCOMPATIBLE_TYPES = [
|
|
548
|
+
"chart",
|
|
549
|
+
"dataTable",
|
|
550
|
+
"interactive",
|
|
551
|
+
"streamable",
|
|
552
|
+
"video",
|
|
553
|
+
"iframe",
|
|
554
|
+
"form",
|
|
555
|
+
"carousel",
|
|
556
|
+
"tabs",
|
|
557
|
+
"accordion",
|
|
558
|
+
"map"
|
|
559
|
+
];
|
|
560
|
+
var validateEmailBlocks = (blocks) => {
|
|
561
|
+
blocks.forEach((block) => {
|
|
562
|
+
if (EMAIL_INCOMPATIBLE_TYPES.includes(block.slug)) {
|
|
563
|
+
console.warn(`\u26A0\uFE0F Block "${block.slug}" may not be email-compatible. Consider creating an email-specific version.`);
|
|
564
|
+
}
|
|
565
|
+
const hasComplexFields = block.fields?.some((field) => {
|
|
566
|
+
const complexTypes = ["code", "json", "richText", "blocks", "array"];
|
|
567
|
+
return complexTypes.includes(field.type);
|
|
568
|
+
});
|
|
569
|
+
if (hasComplexFields) {
|
|
570
|
+
console.warn(`\u26A0\uFE0F Block "${block.slug}" contains complex field types that may not render consistently in email clients.`);
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
};
|
|
574
|
+
var createEmailSafeBlocks = (customBlocks = []) => {
|
|
575
|
+
validateEmailBlocks(customBlocks);
|
|
576
|
+
const baseBlocks = [
|
|
577
|
+
{
|
|
578
|
+
slug: "button",
|
|
579
|
+
fields: [
|
|
580
|
+
{
|
|
581
|
+
name: "text",
|
|
582
|
+
type: "text",
|
|
583
|
+
label: "Button Text",
|
|
584
|
+
required: true
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
name: "url",
|
|
588
|
+
type: "text",
|
|
589
|
+
label: "Button URL",
|
|
590
|
+
required: true,
|
|
591
|
+
admin: {
|
|
592
|
+
description: "Enter the full URL (including https://)"
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
name: "style",
|
|
597
|
+
type: "select",
|
|
598
|
+
label: "Button Style",
|
|
599
|
+
defaultValue: "primary",
|
|
600
|
+
options: [
|
|
601
|
+
{ label: "Primary", value: "primary" },
|
|
602
|
+
{ label: "Secondary", value: "secondary" },
|
|
603
|
+
{ label: "Outline", value: "outline" }
|
|
604
|
+
]
|
|
605
|
+
}
|
|
606
|
+
],
|
|
607
|
+
interfaceName: "EmailButton",
|
|
608
|
+
labels: {
|
|
609
|
+
singular: "Button",
|
|
610
|
+
plural: "Buttons"
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
slug: "divider",
|
|
615
|
+
fields: [
|
|
616
|
+
{
|
|
617
|
+
name: "style",
|
|
618
|
+
type: "select",
|
|
619
|
+
label: "Divider Style",
|
|
620
|
+
defaultValue: "solid",
|
|
621
|
+
options: [
|
|
622
|
+
{ label: "Solid", value: "solid" },
|
|
623
|
+
{ label: "Dashed", value: "dashed" },
|
|
624
|
+
{ label: "Dotted", value: "dotted" }
|
|
625
|
+
]
|
|
626
|
+
}
|
|
627
|
+
],
|
|
628
|
+
interfaceName: "EmailDivider",
|
|
629
|
+
labels: {
|
|
630
|
+
singular: "Divider",
|
|
631
|
+
plural: "Dividers"
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
];
|
|
635
|
+
return [
|
|
636
|
+
...baseBlocks,
|
|
637
|
+
...customBlocks
|
|
638
|
+
];
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
// src/fields/emailContent.ts
|
|
545
642
|
var createEmailSafeFeatures = (additionalBlocks) => {
|
|
546
643
|
const baseBlocks = [
|
|
547
644
|
{
|
|
@@ -679,16 +776,89 @@ var createEmailSafeFeatures = (additionalBlocks) => {
|
|
|
679
776
|
})
|
|
680
777
|
];
|
|
681
778
|
};
|
|
779
|
+
var createEmailLexicalEditor = (customBlocks = []) => {
|
|
780
|
+
const emailSafeBlocks = createEmailSafeBlocks(customBlocks);
|
|
781
|
+
return (0, import_richtext_lexical.lexicalEditor)({
|
|
782
|
+
features: [
|
|
783
|
+
// Toolbars
|
|
784
|
+
(0, import_richtext_lexical.FixedToolbarFeature)(),
|
|
785
|
+
(0, import_richtext_lexical.InlineToolbarFeature)(),
|
|
786
|
+
// Basic text formatting
|
|
787
|
+
(0, import_richtext_lexical.BoldFeature)(),
|
|
788
|
+
(0, import_richtext_lexical.ItalicFeature)(),
|
|
789
|
+
(0, import_richtext_lexical.UnderlineFeature)(),
|
|
790
|
+
(0, import_richtext_lexical.StrikethroughFeature)(),
|
|
791
|
+
// Links with enhanced configuration
|
|
792
|
+
(0, import_richtext_lexical.LinkFeature)({
|
|
793
|
+
fields: [
|
|
794
|
+
{
|
|
795
|
+
name: "url",
|
|
796
|
+
type: "text",
|
|
797
|
+
required: true,
|
|
798
|
+
admin: {
|
|
799
|
+
description: "Enter the full URL (including https://)"
|
|
800
|
+
}
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
name: "newTab",
|
|
804
|
+
type: "checkbox",
|
|
805
|
+
label: "Open in new tab",
|
|
806
|
+
defaultValue: false
|
|
807
|
+
}
|
|
808
|
+
]
|
|
809
|
+
}),
|
|
810
|
+
// Lists
|
|
811
|
+
(0, import_richtext_lexical.OrderedListFeature)(),
|
|
812
|
+
(0, import_richtext_lexical.UnorderedListFeature)(),
|
|
813
|
+
// Headings - limited to h1, h2, h3 for email compatibility
|
|
814
|
+
(0, import_richtext_lexical.HeadingFeature)({
|
|
815
|
+
enabledHeadingSizes: ["h1", "h2", "h3"]
|
|
816
|
+
}),
|
|
817
|
+
// Basic paragraph and alignment
|
|
818
|
+
(0, import_richtext_lexical.ParagraphFeature)(),
|
|
819
|
+
(0, import_richtext_lexical.AlignFeature)(),
|
|
820
|
+
// Blockquotes
|
|
821
|
+
(0, import_richtext_lexical.BlockquoteFeature)(),
|
|
822
|
+
// Upload feature for images
|
|
823
|
+
(0, import_richtext_lexical.UploadFeature)({
|
|
824
|
+
collections: {
|
|
825
|
+
media: {
|
|
826
|
+
fields: [
|
|
827
|
+
{
|
|
828
|
+
name: "caption",
|
|
829
|
+
type: "text",
|
|
830
|
+
admin: {
|
|
831
|
+
description: "Optional caption for the image"
|
|
832
|
+
}
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
name: "altText",
|
|
836
|
+
type: "text",
|
|
837
|
+
label: "Alt Text",
|
|
838
|
+
required: true,
|
|
839
|
+
admin: {
|
|
840
|
+
description: "Alternative text for accessibility and when image cannot be displayed"
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
]
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}),
|
|
847
|
+
// Email-safe blocks (processed server-side)
|
|
848
|
+
(0, import_richtext_lexical.BlocksFeature)({
|
|
849
|
+
blocks: emailSafeBlocks
|
|
850
|
+
})
|
|
851
|
+
]
|
|
852
|
+
});
|
|
853
|
+
};
|
|
682
854
|
var emailSafeFeatures = createEmailSafeFeatures();
|
|
683
855
|
var createEmailContentField = (overrides) => {
|
|
684
|
-
const
|
|
856
|
+
const editor = overrides?.editor || createEmailLexicalEditor(overrides?.additionalBlocks);
|
|
685
857
|
return {
|
|
686
858
|
name: "content",
|
|
687
859
|
type: "richText",
|
|
688
860
|
required: true,
|
|
689
|
-
editor
|
|
690
|
-
features
|
|
691
|
-
}),
|
|
861
|
+
editor,
|
|
692
862
|
admin: {
|
|
693
863
|
description: "Email content with limited formatting for compatibility",
|
|
694
864
|
...overrides?.admin
|
|
@@ -1040,13 +1210,15 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1040
1210
|
}
|
|
1041
1211
|
},
|
|
1042
1212
|
// Apply content field customization if provided
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1213
|
+
// Process blocks server-side to avoid client serialization issues
|
|
1214
|
+
(() => {
|
|
1215
|
+
const emailEditor = createEmailLexicalEditor(customizations?.customBlocks);
|
|
1216
|
+
const baseField = createEmailContentField({
|
|
1217
|
+
admin: { description: "Email content" },
|
|
1218
|
+
editor: emailEditor
|
|
1219
|
+
});
|
|
1220
|
+
return customizations?.fieldOverrides?.content ? customizations.fieldOverrides.content(baseField) : baseField;
|
|
1221
|
+
})()
|
|
1050
1222
|
]
|
|
1051
1223
|
},
|
|
1052
1224
|
{
|