@delmaredigital/payload-puck 0.1.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/LICENSE +73 -0
- package/README.md +1580 -0
- package/dist/AccordionClient.d.mts +24 -0
- package/dist/AccordionClient.d.ts +24 -0
- package/dist/AccordionClient.js +786 -0
- package/dist/AccordionClient.js.map +1 -0
- package/dist/AccordionClient.mjs +784 -0
- package/dist/AccordionClient.mjs.map +1 -0
- package/dist/AnimatedWrapper.d.mts +30 -0
- package/dist/AnimatedWrapper.d.ts +30 -0
- package/dist/AnimatedWrapper.js +379 -0
- package/dist/AnimatedWrapper.js.map +1 -0
- package/dist/AnimatedWrapper.mjs +377 -0
- package/dist/AnimatedWrapper.mjs.map +1 -0
- package/dist/admin/client.d.mts +108 -0
- package/dist/admin/client.d.ts +108 -0
- package/dist/admin/client.js +177 -0
- package/dist/admin/client.js.map +1 -0
- package/dist/admin/client.mjs +173 -0
- package/dist/admin/client.mjs.map +1 -0
- package/dist/admin/index.d.mts +157 -0
- package/dist/admin/index.d.ts +157 -0
- package/dist/admin/index.js +31 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/index.mjs +29 -0
- package/dist/admin/index.mjs.map +1 -0
- package/dist/api/index.d.mts +460 -0
- package/dist/api/index.d.ts +460 -0
- package/dist/api/index.js +588 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/index.mjs +578 -0
- package/dist/api/index.mjs.map +1 -0
- package/dist/components/index.css +339 -0
- package/dist/components/index.css.map +1 -0
- package/dist/components/index.d.mts +222 -0
- package/dist/components/index.d.ts +222 -0
- package/dist/components/index.js +9177 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +9130 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/config/config.editor.css +339 -0
- package/dist/config/config.editor.css.map +1 -0
- package/dist/config/config.editor.d.mts +153 -0
- package/dist/config/config.editor.d.ts +153 -0
- package/dist/config/config.editor.js +9400 -0
- package/dist/config/config.editor.js.map +1 -0
- package/dist/config/config.editor.mjs +9368 -0
- package/dist/config/config.editor.mjs.map +1 -0
- package/dist/config/index.d.mts +68 -0
- package/dist/config/index.d.ts +68 -0
- package/dist/config/index.js +2017 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +1991 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/editor/index.d.mts +784 -0
- package/dist/editor/index.d.ts +784 -0
- package/dist/editor/index.js +4517 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/editor/index.mjs +4483 -0
- package/dist/editor/index.mjs.map +1 -0
- package/dist/fields/index.css +339 -0
- package/dist/fields/index.css.map +1 -0
- package/dist/fields/index.d.mts +600 -0
- package/dist/fields/index.d.ts +600 -0
- package/dist/fields/index.js +7739 -0
- package/dist/fields/index.js.map +1 -0
- package/dist/fields/index.mjs +7590 -0
- package/dist/fields/index.mjs.map +1 -0
- package/dist/index-CQu6SzDg.d.mts +327 -0
- package/dist/index-CoUQnyC3.d.ts +327 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +569 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +555 -0
- package/dist/index.mjs.map +1 -0
- package/dist/layouts/index.d.mts +96 -0
- package/dist/layouts/index.d.ts +96 -0
- package/dist/layouts/index.js +394 -0
- package/dist/layouts/index.js.map +1 -0
- package/dist/layouts/index.mjs +378 -0
- package/dist/layouts/index.mjs.map +1 -0
- package/dist/plugin/index.d.mts +289 -0
- package/dist/plugin/index.d.ts +289 -0
- package/dist/plugin/index.js +569 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/index.mjs +555 -0
- package/dist/plugin/index.mjs.map +1 -0
- package/dist/render/index.d.mts +109 -0
- package/dist/render/index.d.ts +109 -0
- package/dist/render/index.js +2146 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/index.mjs +2123 -0
- package/dist/render/index.mjs.map +1 -0
- package/dist/shared-DMAF1AcH.d.mts +545 -0
- package/dist/shared-DMAF1AcH.d.ts +545 -0
- package/dist/theme/index.d.mts +155 -0
- package/dist/theme/index.d.ts +155 -0
- package/dist/theme/index.js +201 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme/index.mjs +186 -0
- package/dist/theme/index.mjs.map +1 -0
- package/dist/types-D7D3rZ1J.d.mts +116 -0
- package/dist/types-D7D3rZ1J.d.ts +116 -0
- package/dist/types-_6MvjyKv.d.mts +104 -0
- package/dist/types-_6MvjyKv.d.ts +104 -0
- package/dist/utils/index.d.mts +267 -0
- package/dist/utils/index.d.ts +267 -0
- package/dist/utils/index.js +426 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +412 -0
- package/dist/utils/index.mjs.map +1 -0
- package/dist/utils-DaRs9t0J.d.mts +85 -0
- package/dist/utils-gAvt0Vhw.d.ts +85 -0
- package/examples/README.md +240 -0
- package/examples/api/puck/pages/[id]/route.ts +64 -0
- package/examples/api/puck/pages/[id]/versions/route.ts +47 -0
- package/examples/api/puck/pages/route.ts +45 -0
- package/examples/app/(frontend)/page.tsx +94 -0
- package/examples/app/[...slug]/page.tsx +101 -0
- package/examples/app/pages/[id]/edit/page.tsx +148 -0
- package/examples/components/CustomBanner.tsx +368 -0
- package/examples/config/custom-config.ts +223 -0
- package/examples/config/payload.config.example.ts +64 -0
- package/examples/lib/puck-layouts.ts +258 -0
- package/examples/lib/puck-theme.ts +94 -0
- package/examples/styles/puck-theme.css +171 -0
- package/package.json +157 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/layouts/defaults.ts
|
|
4
|
+
var defaultLayout = {
|
|
5
|
+
value: "default",
|
|
6
|
+
label: "Default",
|
|
7
|
+
description: "Standard page layout with contained content width",
|
|
8
|
+
classes: {
|
|
9
|
+
wrapper: "",
|
|
10
|
+
container: "mx-auto px-4 sm:px-6 lg:px-8",
|
|
11
|
+
content: ""
|
|
12
|
+
},
|
|
13
|
+
maxWidth: "1200px",
|
|
14
|
+
fullWidth: false
|
|
15
|
+
};
|
|
16
|
+
var landingLayout = {
|
|
17
|
+
value: "landing",
|
|
18
|
+
label: "Landing",
|
|
19
|
+
description: "Full-width sections with no global container constraints",
|
|
20
|
+
classes: {
|
|
21
|
+
wrapper: "",
|
|
22
|
+
container: "",
|
|
23
|
+
content: ""
|
|
24
|
+
},
|
|
25
|
+
fullWidth: true
|
|
26
|
+
};
|
|
27
|
+
var fullWidthLayout = {
|
|
28
|
+
value: "full-width",
|
|
29
|
+
label: "Full Width",
|
|
30
|
+
description: "Content spans the full viewport width",
|
|
31
|
+
classes: {
|
|
32
|
+
wrapper: "w-full",
|
|
33
|
+
container: "w-full",
|
|
34
|
+
content: ""
|
|
35
|
+
},
|
|
36
|
+
maxWidth: "100%",
|
|
37
|
+
fullWidth: true
|
|
38
|
+
};
|
|
39
|
+
var DEFAULT_LAYOUTS = [
|
|
40
|
+
defaultLayout,
|
|
41
|
+
landingLayout,
|
|
42
|
+
fullWidthLayout
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
// src/layouts/utils.ts
|
|
46
|
+
function layoutsToPayloadOptions(layouts) {
|
|
47
|
+
return layouts.map(({ value, label }) => ({
|
|
48
|
+
label,
|
|
49
|
+
value
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/plugin/fields/index.ts
|
|
54
|
+
var puckDataField = {
|
|
55
|
+
name: "puckData",
|
|
56
|
+
type: "json",
|
|
57
|
+
admin: {
|
|
58
|
+
hidden: true,
|
|
59
|
+
description: "Puck editor data - managed via visual editor"
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
function createEditorVersionField(defaultValue = "puck", sidebar = true, legacyBlocksFieldName = "layout") {
|
|
63
|
+
return {
|
|
64
|
+
name: "editorVersion",
|
|
65
|
+
type: "select",
|
|
66
|
+
// NO defaultValue here - we use the hook to intelligently detect the value
|
|
67
|
+
// This prevents migrations from blindly setting 'puck' on legacy pages
|
|
68
|
+
options: [
|
|
69
|
+
{ label: "Legacy (Payload Blocks)", value: "legacy" },
|
|
70
|
+
{ label: "Puck Visual Editor", value: "puck" }
|
|
71
|
+
],
|
|
72
|
+
admin: {
|
|
73
|
+
position: sidebar ? "sidebar" : void 0,
|
|
74
|
+
description: "Which editor was used to create this page"
|
|
75
|
+
},
|
|
76
|
+
hooks: {
|
|
77
|
+
beforeValidate: [
|
|
78
|
+
({ value, data, operation }) => {
|
|
79
|
+
if (value) return value;
|
|
80
|
+
const legacyBlocks = data?.[legacyBlocksFieldName];
|
|
81
|
+
const hasLegacyBlocks = Array.isArray(legacyBlocks) && legacyBlocks.length > 0;
|
|
82
|
+
const puckData = data?.puckData;
|
|
83
|
+
const hasPuckData = puckData?.content && Array.isArray(puckData.content) && puckData.content.length > 0;
|
|
84
|
+
if (hasLegacyBlocks && !hasPuckData) {
|
|
85
|
+
return "legacy";
|
|
86
|
+
}
|
|
87
|
+
if (hasPuckData) {
|
|
88
|
+
return "puck";
|
|
89
|
+
}
|
|
90
|
+
return defaultValue;
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
var editorVersionField = createEditorVersionField("puck", true);
|
|
97
|
+
function createPageLayoutField(layouts = DEFAULT_LAYOUTS, sidebar = true) {
|
|
98
|
+
return {
|
|
99
|
+
name: "pageLayout",
|
|
100
|
+
type: "select",
|
|
101
|
+
required: true,
|
|
102
|
+
defaultValue: "default",
|
|
103
|
+
options: layoutsToPayloadOptions(layouts),
|
|
104
|
+
admin: {
|
|
105
|
+
position: sidebar ? "sidebar" : void 0,
|
|
106
|
+
description: "Overall page structure and layout style"
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
var pageLayoutField = createPageLayoutField();
|
|
111
|
+
var isHomepageField = {
|
|
112
|
+
name: "isHomepage",
|
|
113
|
+
type: "checkbox",
|
|
114
|
+
defaultValue: false,
|
|
115
|
+
admin: {
|
|
116
|
+
position: "sidebar",
|
|
117
|
+
description: "Mark this page as the homepage"
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var seoFieldGroup = {
|
|
121
|
+
name: "meta",
|
|
122
|
+
type: "group",
|
|
123
|
+
label: "SEO",
|
|
124
|
+
admin: {
|
|
125
|
+
position: "sidebar"
|
|
126
|
+
},
|
|
127
|
+
fields: [
|
|
128
|
+
{
|
|
129
|
+
name: "title",
|
|
130
|
+
type: "text",
|
|
131
|
+
label: "Meta Title",
|
|
132
|
+
admin: {
|
|
133
|
+
description: "Override the page title for search engines"
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "description",
|
|
138
|
+
type: "textarea",
|
|
139
|
+
label: "Meta Description",
|
|
140
|
+
admin: {
|
|
141
|
+
description: "Description shown in search engine results"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: "image",
|
|
146
|
+
type: "upload",
|
|
147
|
+
relationTo: "media",
|
|
148
|
+
label: "Open Graph Image",
|
|
149
|
+
admin: {
|
|
150
|
+
description: "Image shown when sharing on social media"
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "noindex",
|
|
155
|
+
type: "checkbox",
|
|
156
|
+
label: "No Index",
|
|
157
|
+
defaultValue: false,
|
|
158
|
+
admin: {
|
|
159
|
+
description: "Prevent search engines from indexing this page"
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: "nofollow",
|
|
164
|
+
type: "checkbox",
|
|
165
|
+
label: "No Follow",
|
|
166
|
+
defaultValue: false,
|
|
167
|
+
admin: {
|
|
168
|
+
description: "Prevent search engines from following links on this page"
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "excludeFromSitemap",
|
|
173
|
+
type: "checkbox",
|
|
174
|
+
label: "Exclude from Sitemap",
|
|
175
|
+
defaultValue: false,
|
|
176
|
+
admin: {
|
|
177
|
+
description: "Exclude this page from the XML sitemap"
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
};
|
|
182
|
+
var DEFAULT_CONVERSION_TYPES = [
|
|
183
|
+
{ label: "Lead", value: "lead" },
|
|
184
|
+
{ label: "Registration", value: "registration" },
|
|
185
|
+
{ label: "Purchase", value: "purchase" },
|
|
186
|
+
{ label: "Donation", value: "donation" },
|
|
187
|
+
{ label: "Newsletter Signup", value: "newsletter" },
|
|
188
|
+
{ label: "Contact Form", value: "contact" },
|
|
189
|
+
{ label: "Custom", value: "custom" }
|
|
190
|
+
];
|
|
191
|
+
function createConversionFieldGroup(conversionTypes = DEFAULT_CONVERSION_TYPES, sidebar = true) {
|
|
192
|
+
return {
|
|
193
|
+
name: "conversionTracking",
|
|
194
|
+
type: "group",
|
|
195
|
+
label: "Conversion Tracking",
|
|
196
|
+
admin: {
|
|
197
|
+
position: sidebar ? "sidebar" : void 0,
|
|
198
|
+
description: "Configure conversion tracking for analytics"
|
|
199
|
+
},
|
|
200
|
+
fields: [
|
|
201
|
+
{
|
|
202
|
+
name: "isConversionPage",
|
|
203
|
+
type: "checkbox",
|
|
204
|
+
label: "Is Conversion Page",
|
|
205
|
+
defaultValue: false,
|
|
206
|
+
admin: {
|
|
207
|
+
description: "Check this if this page represents a completed conversion (e.g., thank you page)"
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: "conversionType",
|
|
212
|
+
type: "select",
|
|
213
|
+
label: "Conversion Type",
|
|
214
|
+
options: conversionTypes,
|
|
215
|
+
admin: {
|
|
216
|
+
description: "Type of conversion this page represents",
|
|
217
|
+
condition: (data, siblingData) => siblingData?.isConversionPage === true
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "conversionValue",
|
|
222
|
+
type: "number",
|
|
223
|
+
label: "Conversion Value",
|
|
224
|
+
defaultValue: 0,
|
|
225
|
+
admin: {
|
|
226
|
+
description: "Monetary value of this conversion (0 for non-monetary conversions)",
|
|
227
|
+
condition: (data, siblingData) => siblingData?.isConversionPage === true
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
var conversionFieldGroup = createConversionFieldGroup();
|
|
234
|
+
function getPuckFields(options = {}) {
|
|
235
|
+
const {
|
|
236
|
+
includeSEO = true,
|
|
237
|
+
includeConversion = false,
|
|
238
|
+
includeEditorVersion = true,
|
|
239
|
+
includePageLayout = true,
|
|
240
|
+
includeIsHomepage = false,
|
|
241
|
+
layouts = DEFAULT_LAYOUTS,
|
|
242
|
+
defaultEditorVersion = "puck",
|
|
243
|
+
legacyBlocksFieldName = "layout",
|
|
244
|
+
sidebarPosition = true,
|
|
245
|
+
conversionTypeOptions
|
|
246
|
+
} = options;
|
|
247
|
+
const fields = [];
|
|
248
|
+
fields.push(puckDataField);
|
|
249
|
+
if (includeEditorVersion) {
|
|
250
|
+
fields.push(createEditorVersionField(defaultEditorVersion, sidebarPosition, legacyBlocksFieldName));
|
|
251
|
+
}
|
|
252
|
+
if (includePageLayout) {
|
|
253
|
+
fields.push(createPageLayoutField(layouts, sidebarPosition));
|
|
254
|
+
}
|
|
255
|
+
if (includeIsHomepage) {
|
|
256
|
+
fields.push(isHomepageField);
|
|
257
|
+
}
|
|
258
|
+
if (includeSEO) {
|
|
259
|
+
fields.push(seoFieldGroup);
|
|
260
|
+
}
|
|
261
|
+
if (includeConversion) {
|
|
262
|
+
fields.push(createConversionFieldGroup(conversionTypeOptions, sidebarPosition));
|
|
263
|
+
}
|
|
264
|
+
return fields;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/plugin/collections/Pages.ts
|
|
268
|
+
var defaultAccess = () => true;
|
|
269
|
+
function generatePagesCollection(slug, options) {
|
|
270
|
+
const {
|
|
271
|
+
collectionOverrides = {},
|
|
272
|
+
access = {},
|
|
273
|
+
layouts = DEFAULT_LAYOUTS,
|
|
274
|
+
additionalFields = []
|
|
275
|
+
} = options;
|
|
276
|
+
const baseFields = [
|
|
277
|
+
// Core Fields (title and slug with duplication hooks - unique to collection generation)
|
|
278
|
+
{
|
|
279
|
+
name: "title",
|
|
280
|
+
type: "text",
|
|
281
|
+
required: true,
|
|
282
|
+
hooks: {
|
|
283
|
+
beforeDuplicate: [
|
|
284
|
+
({ value }) => {
|
|
285
|
+
if (!value) return value;
|
|
286
|
+
const copyMatch = value.match(/^(.+) \(Copy(?: (\d+))?\)$/);
|
|
287
|
+
if (copyMatch) {
|
|
288
|
+
const baseName = copyMatch[1];
|
|
289
|
+
const copyNum = copyMatch[2] ? parseInt(copyMatch[2], 10) + 1 : 2;
|
|
290
|
+
return `${baseName} (Copy ${copyNum})`;
|
|
291
|
+
}
|
|
292
|
+
return `${value} (Copy)`;
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: "slug",
|
|
299
|
+
type: "text",
|
|
300
|
+
required: true,
|
|
301
|
+
unique: true,
|
|
302
|
+
index: true,
|
|
303
|
+
admin: {
|
|
304
|
+
position: "sidebar",
|
|
305
|
+
description: "URL path for this page (auto-generated from title)"
|
|
306
|
+
},
|
|
307
|
+
hooks: {
|
|
308
|
+
beforeValidate: [
|
|
309
|
+
({ data, value }) => {
|
|
310
|
+
if (data && !value && data.title) {
|
|
311
|
+
return data.title.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim();
|
|
312
|
+
}
|
|
313
|
+
return value;
|
|
314
|
+
}
|
|
315
|
+
],
|
|
316
|
+
beforeDuplicate: [
|
|
317
|
+
({ value }) => {
|
|
318
|
+
if (!value) return value;
|
|
319
|
+
const copyMatch = value.match(/^(.+)-copy(?:-(\d+))?$/);
|
|
320
|
+
if (copyMatch) {
|
|
321
|
+
const baseName = copyMatch[1];
|
|
322
|
+
const copyNum = copyMatch[2] ? parseInt(copyMatch[2], 10) + 1 : 2;
|
|
323
|
+
return `${baseName}-copy-${copyNum}`;
|
|
324
|
+
}
|
|
325
|
+
return `${value}-copy`;
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
// Page Layout
|
|
331
|
+
createPageLayoutField(layouts, true),
|
|
332
|
+
// Editor Version
|
|
333
|
+
createEditorVersionField("puck", true),
|
|
334
|
+
// Homepage Flag
|
|
335
|
+
isHomepageField,
|
|
336
|
+
// Puck Data (hidden - managed via visual editor)
|
|
337
|
+
puckDataField,
|
|
338
|
+
// SEO Fields
|
|
339
|
+
seoFieldGroup,
|
|
340
|
+
// Conversion Tracking Fields
|
|
341
|
+
conversionFieldGroup,
|
|
342
|
+
// Additional fields from options
|
|
343
|
+
...additionalFields
|
|
344
|
+
];
|
|
345
|
+
return {
|
|
346
|
+
slug,
|
|
347
|
+
admin: {
|
|
348
|
+
useAsTitle: "title",
|
|
349
|
+
group: "Content",
|
|
350
|
+
defaultColumns: ["title", "slug", "_status", "updatedAt"],
|
|
351
|
+
...collectionOverrides.admin ?? {}
|
|
352
|
+
},
|
|
353
|
+
access: {
|
|
354
|
+
read: access.read ?? defaultAccess,
|
|
355
|
+
create: access.create ?? defaultAccess,
|
|
356
|
+
update: access.update ?? defaultAccess,
|
|
357
|
+
delete: access.delete ?? defaultAccess,
|
|
358
|
+
...collectionOverrides.access ?? {}
|
|
359
|
+
},
|
|
360
|
+
versions: typeof collectionOverrides.versions === "object" ? { drafts: true, ...collectionOverrides.versions } : { drafts: true },
|
|
361
|
+
fields: baseFields,
|
|
362
|
+
...collectionOverrides,
|
|
363
|
+
// Ensure fields aren't overwritten by collectionOverrides
|
|
364
|
+
...collectionOverrides.fields && {
|
|
365
|
+
fields: [...baseFields, ...collectionOverrides.fields]
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/collections/Templates.ts
|
|
371
|
+
var TemplatesCollection = {
|
|
372
|
+
slug: "puck-templates",
|
|
373
|
+
admin: {
|
|
374
|
+
useAsTitle: "name",
|
|
375
|
+
group: "Puck",
|
|
376
|
+
defaultColumns: ["name", "category", "updatedAt"],
|
|
377
|
+
description: "Reusable component templates for the visual editor"
|
|
378
|
+
},
|
|
379
|
+
access: {
|
|
380
|
+
read: () => true,
|
|
381
|
+
create: ({ req }) => !!req.user,
|
|
382
|
+
update: ({ req }) => !!req.user,
|
|
383
|
+
delete: ({ req }) => !!req.user
|
|
384
|
+
},
|
|
385
|
+
fields: [
|
|
386
|
+
{
|
|
387
|
+
name: "name",
|
|
388
|
+
type: "text",
|
|
389
|
+
required: true,
|
|
390
|
+
admin: {
|
|
391
|
+
description: "A descriptive name for this template"
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: "description",
|
|
396
|
+
type: "textarea",
|
|
397
|
+
admin: {
|
|
398
|
+
description: "Optional description of what this template contains"
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
name: "category",
|
|
403
|
+
type: "text",
|
|
404
|
+
admin: {
|
|
405
|
+
description: 'Category for organizing templates (e.g., "Hero", "Footer", "CTA")'
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: "content",
|
|
410
|
+
type: "json",
|
|
411
|
+
required: true,
|
|
412
|
+
admin: {
|
|
413
|
+
description: "Serialized Puck component data"
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "thumbnail",
|
|
418
|
+
type: "text",
|
|
419
|
+
admin: {
|
|
420
|
+
description: "Optional thumbnail URL for template preview"
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
],
|
|
424
|
+
timestamps: true
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// src/plugin/index.ts
|
|
428
|
+
function getExistingFieldNames(fields) {
|
|
429
|
+
const names = /* @__PURE__ */ new Set();
|
|
430
|
+
function addFieldNames(fieldsToCheck) {
|
|
431
|
+
for (const field of fieldsToCheck) {
|
|
432
|
+
if ("name" in field && field.name) {
|
|
433
|
+
names.add(field.name);
|
|
434
|
+
}
|
|
435
|
+
if ("fields" in field && Array.isArray(field.fields)) {
|
|
436
|
+
addFieldNames(field.fields);
|
|
437
|
+
}
|
|
438
|
+
if (field.type === "tabs" && "tabs" in field && Array.isArray(field.tabs)) {
|
|
439
|
+
for (const tab of field.tabs) {
|
|
440
|
+
if ("fields" in tab && Array.isArray(tab.fields)) {
|
|
441
|
+
addFieldNames(tab.fields);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
addFieldNames(fields);
|
|
448
|
+
return names;
|
|
449
|
+
}
|
|
450
|
+
function filterExistingFields(fieldsToAdd, existingNames) {
|
|
451
|
+
return fieldsToAdd.filter((field) => {
|
|
452
|
+
if ("name" in field && field.name) {
|
|
453
|
+
return !existingNames.has(field.name);
|
|
454
|
+
}
|
|
455
|
+
return true;
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
function generatePuckEditField(collectionSlug, adminConfig = {}) {
|
|
459
|
+
const {
|
|
460
|
+
editorPathPattern = "/pages/{id}/edit",
|
|
461
|
+
buttonLabel = "Edit with Puck",
|
|
462
|
+
buttonPosition
|
|
463
|
+
// undefined = main area (default), 'sidebar' = sidebar
|
|
464
|
+
} = adminConfig;
|
|
465
|
+
return {
|
|
466
|
+
name: "puckEdit",
|
|
467
|
+
type: "ui",
|
|
468
|
+
admin: {
|
|
469
|
+
// Only set position if explicitly specified (sidebar)
|
|
470
|
+
// undefined means main form area in Payload
|
|
471
|
+
...buttonPosition && { position: buttonPosition },
|
|
472
|
+
components: {
|
|
473
|
+
Field: "@delmaredigital/payload-puck/admin/client#EditWithPuckButton",
|
|
474
|
+
Cell: "@delmaredigital/payload-puck/admin/client#EditWithPuckCell"
|
|
475
|
+
},
|
|
476
|
+
custom: {
|
|
477
|
+
collectionSlug,
|
|
478
|
+
editorPathPattern,
|
|
479
|
+
label: buttonLabel
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function createPuckPlugin(options = {}) {
|
|
485
|
+
const {
|
|
486
|
+
pagesCollection = "pages",
|
|
487
|
+
autoGenerateCollection = true,
|
|
488
|
+
admin: adminConfig = {}
|
|
489
|
+
} = options;
|
|
490
|
+
const { addEditButton = true } = adminConfig;
|
|
491
|
+
return (incomingConfig) => {
|
|
492
|
+
let collections = incomingConfig.collections || [];
|
|
493
|
+
const templatesCollectionExists = collections.some(
|
|
494
|
+
(c) => c.slug === "puck-templates"
|
|
495
|
+
);
|
|
496
|
+
if (!templatesCollectionExists) {
|
|
497
|
+
collections = [...collections, TemplatesCollection];
|
|
498
|
+
}
|
|
499
|
+
if (autoGenerateCollection) {
|
|
500
|
+
const existingCollectionIndex = collections.findIndex(
|
|
501
|
+
(c) => c.slug === pagesCollection
|
|
502
|
+
);
|
|
503
|
+
const editButtonField = addEditButton ? [generatePuckEditField(pagesCollection, adminConfig)] : [];
|
|
504
|
+
if (existingCollectionIndex >= 0) {
|
|
505
|
+
const existingCollection = collections[existingCollectionIndex];
|
|
506
|
+
const existingFields = existingCollection.fields || [];
|
|
507
|
+
const existingFieldNames = getExistingFieldNames(existingFields);
|
|
508
|
+
const puckFields = getPuckFields({
|
|
509
|
+
includeSEO: !existingFieldNames.has("meta"),
|
|
510
|
+
includeConversion: !existingFieldNames.has("conversionTracking"),
|
|
511
|
+
includeEditorVersion: !existingFieldNames.has("editorVersion"),
|
|
512
|
+
includePageLayout: !existingFieldNames.has("pageLayout"),
|
|
513
|
+
includeIsHomepage: !existingFieldNames.has("isHomepage"),
|
|
514
|
+
layouts: options.layouts
|
|
515
|
+
});
|
|
516
|
+
const fieldsToAdd = filterExistingFields(puckFields, existingFieldNames);
|
|
517
|
+
const editFieldsToAdd = existingFieldNames.has("puckEdit") ? [] : editButtonField;
|
|
518
|
+
collections = [
|
|
519
|
+
...collections.slice(0, existingCollectionIndex),
|
|
520
|
+
{
|
|
521
|
+
...existingCollection,
|
|
522
|
+
// Ensure drafts are enabled for Puck
|
|
523
|
+
versions: typeof existingCollection.versions === "object" ? { drafts: true, ...existingCollection.versions } : existingCollection.versions ?? { drafts: true },
|
|
524
|
+
fields: [
|
|
525
|
+
...existingFields,
|
|
526
|
+
...fieldsToAdd,
|
|
527
|
+
...editFieldsToAdd
|
|
528
|
+
]
|
|
529
|
+
},
|
|
530
|
+
...collections.slice(existingCollectionIndex + 1)
|
|
531
|
+
];
|
|
532
|
+
} else {
|
|
533
|
+
const generatedCollection = generatePagesCollection(pagesCollection, options);
|
|
534
|
+
collections = [
|
|
535
|
+
...collections,
|
|
536
|
+
{
|
|
537
|
+
...generatedCollection,
|
|
538
|
+
fields: [...generatedCollection.fields, ...editButtonField]
|
|
539
|
+
}
|
|
540
|
+
];
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return {
|
|
544
|
+
...incomingConfig,
|
|
545
|
+
collections,
|
|
546
|
+
onInit: async (payload) => {
|
|
547
|
+
if (incomingConfig.onInit) {
|
|
548
|
+
await incomingConfig.onInit(payload);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
exports.TemplatesCollection = TemplatesCollection;
|
|
556
|
+
exports.conversionFieldGroup = conversionFieldGroup;
|
|
557
|
+
exports.createEditorVersionField = createEditorVersionField;
|
|
558
|
+
exports.createPageLayoutField = createPageLayoutField;
|
|
559
|
+
exports.createPuckPlugin = createPuckPlugin;
|
|
560
|
+
exports.editorVersionField = editorVersionField;
|
|
561
|
+
exports.generatePagesCollection = generatePagesCollection;
|
|
562
|
+
exports.generatePuckEditField = generatePuckEditField;
|
|
563
|
+
exports.getPuckFields = getPuckFields;
|
|
564
|
+
exports.isHomepageField = isHomepageField;
|
|
565
|
+
exports.pageLayoutField = pageLayoutField;
|
|
566
|
+
exports.puckDataField = puckDataField;
|
|
567
|
+
exports.seoFieldGroup = seoFieldGroup;
|
|
568
|
+
//# sourceMappingURL=index.js.map
|
|
569
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/layouts/defaults.ts","../src/layouts/utils.ts","../src/plugin/fields/index.ts","../src/plugin/collections/Pages.ts","../src/collections/Templates.ts","../src/plugin/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Default Layout Definitions\n *\n * These provide sensible defaults for common page layout patterns.\n * Users can override or extend these in their own configuration.\n */\n\nimport type { LayoutDefinition, LayoutConfig } from './types'\n\n/**\n * Default layout - standard content width with padding\n */\nexport const defaultLayout: LayoutDefinition = {\n value: 'default',\n label: 'Default',\n description: 'Standard page layout with contained content width',\n classes: {\n wrapper: '',\n container: 'mx-auto px-4 sm:px-6 lg:px-8',\n content: '',\n },\n maxWidth: '1200px',\n fullWidth: false,\n}\n\n/**\n * Landing layout - optimized for marketing/landing pages\n */\nexport const landingLayout: LayoutDefinition = {\n value: 'landing',\n label: 'Landing',\n description: 'Full-width sections with no global container constraints',\n classes: {\n wrapper: '',\n container: '',\n content: '',\n },\n fullWidth: true,\n}\n\n/**\n * Full width layout - edge-to-edge content\n */\nexport const fullWidthLayout: LayoutDefinition = {\n value: 'full-width',\n label: 'Full Width',\n description: 'Content spans the full viewport width',\n classes: {\n wrapper: 'w-full',\n container: 'w-full',\n content: '',\n },\n maxWidth: '100%',\n fullWidth: true,\n}\n\n/**\n * Narrow layout - ideal for blog posts and articles\n */\nexport const narrowLayout: LayoutDefinition = {\n value: 'narrow',\n label: 'Narrow',\n description: 'Narrow content width for optimal reading experience',\n classes: {\n wrapper: '',\n container: 'mx-auto px-4 sm:px-6',\n content: '',\n },\n maxWidth: '768px',\n fullWidth: false,\n}\n\n/**\n * Wide layout - extra wide content area\n */\nexport const wideLayout: LayoutDefinition = {\n value: 'wide',\n label: 'Wide',\n description: 'Wider content area for dashboards or galleries',\n classes: {\n wrapper: '',\n container: 'mx-auto px-4 sm:px-6 lg:px-8',\n content: '',\n },\n maxWidth: '1440px',\n fullWidth: false,\n}\n\n/**\n * Default layouts included with the plugin\n */\nexport const DEFAULT_LAYOUTS: LayoutDefinition[] = [\n defaultLayout,\n landingLayout,\n fullWidthLayout,\n]\n\n/**\n * Extended layouts for users who want more options\n */\nexport const EXTENDED_LAYOUTS: LayoutDefinition[] = [\n defaultLayout,\n landingLayout,\n fullWidthLayout,\n narrowLayout,\n wideLayout,\n]\n\n/**\n * Default layout configuration\n */\nexport const DEFAULT_LAYOUT_CONFIG: LayoutConfig = {\n layouts: DEFAULT_LAYOUTS,\n defaultLayout: 'default',\n}\n","/**\n * Layout Utilities\n *\n * Functions for working with layout configurations.\n */\n\nimport type { LayoutDefinition, LayoutConfig, LayoutOption } from './types'\nimport { DEFAULT_LAYOUTS, DEFAULT_LAYOUT_CONFIG } from './defaults'\n\n/**\n * Resolves a layout config, merging with defaults if needed\n */\nexport function resolveLayoutConfig(config?: Partial<LayoutConfig>): LayoutConfig {\n if (!config) return DEFAULT_LAYOUT_CONFIG\n\n return {\n layouts: config.layouts ?? DEFAULT_LAYOUTS,\n defaultLayout: config.defaultLayout ?? 'default',\n }\n}\n\n/**\n * Gets a layout definition by value\n */\nexport function getLayout(\n layouts: LayoutDefinition[],\n value: string,\n fallback = 'default'\n): LayoutDefinition | undefined {\n const layout = layouts.find((l) => l.value === value)\n if (layout) return layout\n\n // Try fallback\n if (value !== fallback) {\n return layouts.find((l) => l.value === fallback)\n }\n\n // Return first layout if nothing matches\n return layouts[0]\n}\n\n/**\n * Converts layout definitions to Puck select options\n */\nexport function layoutsToOptions(layouts: LayoutDefinition[]): LayoutOption[] {\n return layouts.map(({ value, label, description }) => ({\n value,\n label,\n description,\n }))\n}\n\n/**\n * Converts layout definitions to Payload select options\n */\nexport function layoutsToPayloadOptions(\n layouts: LayoutDefinition[]\n): Array<{ label: string; value: string }> {\n return layouts.map(({ value, label }) => ({\n label,\n value,\n }))\n}\n\n/**\n * Creates a custom layout definition\n */\nexport function createLayout(\n config: Omit<LayoutDefinition, 'value' | 'label'> & {\n value: string\n label: string\n }\n): LayoutDefinition {\n return {\n ...config,\n }\n}\n\n/**\n * Merges layout configurations\n */\nexport function mergeLayouts(\n base: LayoutDefinition[],\n custom: LayoutDefinition[],\n options?: {\n /** Replace base layouts instead of merging */\n replace?: boolean\n /** Exclude these layout values from base */\n exclude?: string[]\n }\n): LayoutDefinition[] {\n if (options?.replace) {\n return custom\n }\n\n let result = [...base]\n\n // Exclude specified layouts\n if (options?.exclude) {\n result = result.filter((l) => !options.exclude!.includes(l.value))\n }\n\n // Merge/override with custom layouts\n for (const customLayout of custom) {\n const existingIndex = result.findIndex((l) => l.value === customLayout.value)\n if (existingIndex >= 0) {\n result[existingIndex] = customLayout\n } else {\n result.push(customLayout)\n }\n }\n\n return result\n}\n","/**\n * Puck Field Utilities\n *\n * Provides reusable field definitions for adding Puck support to existing collections.\n * Use getPuckFields() for easy integration, or import individual fields for granular control.\n *\n * @example\n * ```typescript\n * // Option 1: Use getPuckFields() for recommended defaults\n * import { getPuckFields } from '@delmaredigital/payload-puck'\n *\n * const Pages: CollectionConfig = {\n * slug: 'pages',\n * fields: [\n * // Your existing fields...\n * ...getPuckFields({ includeSEO: false }),\n * ],\n * }\n *\n * // Option 2: Import individual fields\n * import { puckDataField, editorVersionField } from '@delmaredigital/payload-puck'\n *\n * const Pages: CollectionConfig = {\n * slug: 'pages',\n * fields: [\n * // Your existing fields...\n * puckDataField,\n * editorVersionField,\n * ],\n * }\n * ```\n */\n\nimport type { Field } from 'payload'\nimport type { GetPuckFieldsOptions, ConversionTypeOption } from './types'\nimport type { LayoutDefinition } from '../../layouts/types'\nimport { DEFAULT_LAYOUTS } from '../../layouts/defaults'\nimport { layoutsToPayloadOptions } from '../../layouts/utils'\n\n// =============================================================================\n// Core Fields\n// =============================================================================\n\n/**\n * Puck data field - stores the visual editor JSON data.\n * This field is always hidden in the admin UI as it's managed via the visual editor.\n */\nexport const puckDataField: Field = {\n name: 'puckData',\n type: 'json',\n admin: {\n hidden: true,\n description: 'Puck editor data - managed via visual editor',\n },\n}\n\n/**\n * Creates an editor version field with smart detection for hybrid setups.\n *\n * The field uses a beforeValidate hook to intelligently determine the editor version:\n * - New pages: Default to 'puck' (or custom defaultValue)\n * - Existing pages with legacy blocks but no puckData: Set to 'legacy'\n * - Existing pages with puckData: Set to 'puck'\n *\n * This prevents existing legacy content from being incorrectly marked as 'puck'\n * when the field is first added to a collection.\n *\n * @param defaultValue - The default editor version for NEW pages ('puck' or 'legacy')\n * @param sidebar - Whether to position in the sidebar (default: true)\n * @param legacyBlocksFieldName - Name of the legacy blocks field to check (default: 'layout')\n */\nexport function createEditorVersionField(\n defaultValue: 'legacy' | 'puck' = 'puck',\n sidebar: boolean = true,\n legacyBlocksFieldName: string = 'layout'\n): Field {\n return {\n name: 'editorVersion',\n type: 'select',\n // NO defaultValue here - we use the hook to intelligently detect the value\n // This prevents migrations from blindly setting 'puck' on legacy pages\n options: [\n { label: 'Legacy (Payload Blocks)', value: 'legacy' },\n { label: 'Puck Visual Editor', value: 'puck' },\n ],\n admin: {\n position: sidebar ? 'sidebar' : undefined,\n description: 'Which editor was used to create this page',\n },\n hooks: {\n beforeValidate: [\n ({ value, data, operation }) => {\n // If value is explicitly set (and not null/undefined), keep it\n if (value) return value\n\n // Detect based on content - works for both new and existing documents\n const legacyBlocks = data?.[legacyBlocksFieldName]\n const hasLegacyBlocks = Array.isArray(legacyBlocks) && legacyBlocks.length > 0\n\n const puckData = data?.puckData as { content?: unknown[] } | null | undefined\n const hasPuckData =\n puckData?.content && Array.isArray(puckData.content) && puckData.content.length > 0\n\n // If has legacy blocks but no puck data, it's a legacy page\n if (hasLegacyBlocks && !hasPuckData) {\n return 'legacy'\n }\n\n // If has puck data, it's a puck page\n if (hasPuckData) {\n return 'puck'\n }\n\n // For new documents with no content yet, use configured default\n // For existing documents with no content, also use default\n return defaultValue\n },\n ],\n },\n }\n}\n\n/**\n * Pre-configured editor version field with 'puck' as default.\n * Use createEditorVersionField() for custom configuration.\n */\nexport const editorVersionField: Field = createEditorVersionField('puck', true)\n\n/**\n * Creates a page layout field with custom layouts.\n *\n * @param layouts - Array of layout definitions (defaults to DEFAULT_LAYOUTS)\n * @param sidebar - Whether to position in the sidebar (default: true)\n */\nexport function createPageLayoutField(\n layouts: LayoutDefinition[] = DEFAULT_LAYOUTS,\n sidebar: boolean = true\n): Field {\n return {\n name: 'pageLayout',\n type: 'select',\n required: true,\n defaultValue: 'default',\n options: layoutsToPayloadOptions(layouts),\n admin: {\n position: sidebar ? 'sidebar' : undefined,\n description: 'Overall page structure and layout style',\n },\n }\n}\n\n/**\n * Pre-configured page layout field with DEFAULT_LAYOUTS.\n * Use createPageLayoutField() for custom layouts.\n */\nexport const pageLayoutField: Field = createPageLayoutField()\n\n/**\n * Homepage flag field - marks a page as the site homepage.\n */\nexport const isHomepageField: Field = {\n name: 'isHomepage',\n type: 'checkbox',\n defaultValue: false,\n admin: {\n position: 'sidebar',\n description: 'Mark this page as the homepage',\n },\n}\n\n// =============================================================================\n// Field Groups\n// =============================================================================\n\n/**\n * SEO/Meta field group with all metadata fields.\n * Uses the official @payloadcms/plugin-seo convention: meta.title, meta.description\n *\n * Includes: title, description, image, noindex, nofollow, excludeFromSitemap\n */\nexport const seoFieldGroup: Field = {\n name: 'meta',\n type: 'group',\n label: 'SEO',\n admin: {\n position: 'sidebar',\n },\n fields: [\n {\n name: 'title',\n type: 'text',\n label: 'Meta Title',\n admin: {\n description: 'Override the page title for search engines',\n },\n },\n {\n name: 'description',\n type: 'textarea',\n label: 'Meta Description',\n admin: {\n description: 'Description shown in search engine results',\n },\n },\n {\n name: 'image',\n type: 'upload',\n relationTo: 'media',\n label: 'Open Graph Image',\n admin: {\n description: 'Image shown when sharing on social media',\n },\n },\n {\n name: 'noindex',\n type: 'checkbox',\n label: 'No Index',\n defaultValue: false,\n admin: {\n description: 'Prevent search engines from indexing this page',\n },\n },\n {\n name: 'nofollow',\n type: 'checkbox',\n label: 'No Follow',\n defaultValue: false,\n admin: {\n description: 'Prevent search engines from following links on this page',\n },\n },\n {\n name: 'excludeFromSitemap',\n type: 'checkbox',\n label: 'Exclude from Sitemap',\n defaultValue: false,\n admin: {\n description: 'Exclude this page from the XML sitemap',\n },\n },\n ],\n}\n\n/**\n * Default conversion type options\n */\nexport const DEFAULT_CONVERSION_TYPES: ConversionTypeOption[] = [\n { label: 'Lead', value: 'lead' },\n { label: 'Registration', value: 'registration' },\n { label: 'Purchase', value: 'purchase' },\n { label: 'Donation', value: 'donation' },\n { label: 'Newsletter Signup', value: 'newsletter' },\n { label: 'Contact Form', value: 'contact' },\n { label: 'Custom', value: 'custom' },\n]\n\n/**\n * Creates a conversion tracking field group with custom options.\n *\n * @param conversionTypes - Custom conversion type options (defaults to DEFAULT_CONVERSION_TYPES)\n * @param sidebar - Whether to position in the sidebar (default: true)\n *\n * @example\n * ```typescript\n * // Use default conversion types\n * ...getPuckFields({ includeConversion: true })\n *\n * // Use custom conversion types\n * ...getPuckFields({\n * includeConversion: true,\n * conversionTypeOptions: [\n * { label: 'Registration', value: 'registration' },\n * { label: 'Donation', value: 'donation' },\n * { label: 'Course Start', value: 'course_start' },\n * { label: 'Custom', value: 'custom' },\n * ],\n * })\n * ```\n */\nexport function createConversionFieldGroup(\n conversionTypes: ConversionTypeOption[] = DEFAULT_CONVERSION_TYPES,\n sidebar: boolean = true\n): Field {\n return {\n name: 'conversionTracking',\n type: 'group',\n label: 'Conversion Tracking',\n admin: {\n position: sidebar ? 'sidebar' : undefined,\n description: 'Configure conversion tracking for analytics',\n },\n fields: [\n {\n name: 'isConversionPage',\n type: 'checkbox',\n label: 'Is Conversion Page',\n defaultValue: false,\n admin: {\n description:\n 'Check this if this page represents a completed conversion (e.g., thank you page)',\n },\n },\n {\n name: 'conversionType',\n type: 'select',\n label: 'Conversion Type',\n options: conversionTypes,\n admin: {\n description: 'Type of conversion this page represents',\n condition: (data, siblingData) => siblingData?.isConversionPage === true,\n },\n },\n {\n name: 'conversionValue',\n type: 'number',\n label: 'Conversion Value',\n defaultValue: 0,\n admin: {\n description: 'Monetary value of this conversion (0 for non-monetary conversions)',\n condition: (data, siblingData) => siblingData?.isConversionPage === true,\n },\n },\n ],\n }\n}\n\n/**\n * Pre-configured conversion tracking field group with default options.\n * Use createConversionFieldGroup() for custom conversion types.\n */\nexport const conversionFieldGroup: Field = createConversionFieldGroup()\n\n// =============================================================================\n// Main Utility Function\n// =============================================================================\n\n/**\n * Returns an array of Puck-related field definitions for hybrid collection integration.\n *\n * Use this when you have an existing collection with legacy Payload blocks and want\n * to ADD Puck support without replacing your entire collection configuration.\n *\n * @param options - Configuration options for which fields to include\n * @returns Array of Field definitions to spread into your collection's fields array\n *\n * @example\n * ```typescript\n * import { getPuckFields } from '@delmaredigital/payload-puck'\n *\n * export const Pages: CollectionConfig = {\n * slug: 'pages',\n * fields: [\n * // Your existing fields\n * { name: 'title', type: 'text', required: true },\n * { name: 'slug', type: 'text', required: true },\n *\n * // Legacy Payload blocks (keep these!)\n * { name: 'layout', type: 'blocks', blocks: [...] },\n *\n * // Add Puck fields for hybrid editing\n * ...getPuckFields({\n * includeSEO: false, // You have your own SEO fields\n * includeConversion: true, // Include conversion tracking\n * includeEditorVersion: true,\n * }),\n * ],\n * }\n * ```\n */\nexport function getPuckFields(options: GetPuckFieldsOptions = {}): Field[] {\n const {\n includeSEO = true,\n includeConversion = false,\n includeEditorVersion = true,\n includePageLayout = true,\n includeIsHomepage = false,\n layouts = DEFAULT_LAYOUTS,\n defaultEditorVersion = 'puck',\n legacyBlocksFieldName = 'layout',\n sidebarPosition = true,\n conversionTypeOptions,\n } = options\n\n const fields: Field[] = []\n\n // Core puckData field (always included - this is essential for Puck)\n fields.push(puckDataField)\n\n // Editor version field (discriminator for hybrid rendering)\n // Uses smart detection to preserve legacy pages when field is first added\n if (includeEditorVersion) {\n fields.push(createEditorVersionField(defaultEditorVersion, sidebarPosition, legacyBlocksFieldName))\n }\n\n // Page layout field\n if (includePageLayout) {\n fields.push(createPageLayoutField(layouts, sidebarPosition))\n }\n\n // Homepage flag\n if (includeIsHomepage) {\n fields.push(isHomepageField)\n }\n\n // SEO group\n if (includeSEO) {\n fields.push(seoFieldGroup)\n }\n\n // Conversion tracking group (with optional custom conversion types)\n if (includeConversion) {\n fields.push(createConversionFieldGroup(conversionTypeOptions, sidebarPosition))\n }\n\n return fields\n}\n\n// Re-export types\nexport type { GetPuckFieldsOptions, ConversionTypeOption } from './types'\n","import type { Access, CollectionConfig, Field } from 'payload'\nimport type { PuckPluginOptions } from '../../types'\nimport { DEFAULT_LAYOUTS } from '../../layouts/defaults'\nimport { layoutsToPayloadOptions } from '../../layouts/utils'\nimport {\n puckDataField,\n createEditorVersionField,\n createPageLayoutField,\n isHomepageField,\n seoFieldGroup,\n conversionFieldGroup,\n} from '../fields'\n\n/**\n * Default access function - allows all\n */\nconst defaultAccess: Access = () => true\n\n/**\n * Generates a Pages collection configuration for Puck\n */\nexport function generatePagesCollection(\n slug: string,\n options: PuckPluginOptions\n): CollectionConfig {\n const {\n collectionOverrides = {},\n access = {},\n layouts = DEFAULT_LAYOUTS,\n additionalFields = [],\n } = options\n\n const baseFields: Field[] = [\n // Core Fields (title and slug with duplication hooks - unique to collection generation)\n {\n name: 'title',\n type: 'text',\n required: true,\n hooks: {\n beforeDuplicate: [\n ({ value }) => {\n if (!value) return value\n const copyMatch = value.match(/^(.+) \\(Copy(?: (\\d+))?\\)$/)\n if (copyMatch) {\n const baseName = copyMatch[1]\n const copyNum = copyMatch[2] ? parseInt(copyMatch[2], 10) + 1 : 2\n return `${baseName} (Copy ${copyNum})`\n }\n return `${value} (Copy)`\n },\n ],\n },\n },\n {\n name: 'slug',\n type: 'text',\n required: true,\n unique: true,\n index: true,\n admin: {\n position: 'sidebar',\n description: 'URL path for this page (auto-generated from title)',\n },\n hooks: {\n beforeValidate: [\n ({ data, value }) => {\n if (data && !value && data.title) {\n return (data.title as string)\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/\\s+/g, '-')\n .replace(/-+/g, '-')\n .trim()\n }\n return value\n },\n ],\n beforeDuplicate: [\n ({ value }) => {\n if (!value) return value\n const copyMatch = value.match(/^(.+)-copy(?:-(\\d+))?$/)\n if (copyMatch) {\n const baseName = copyMatch[1]\n const copyNum = copyMatch[2] ? parseInt(copyMatch[2], 10) + 1 : 2\n return `${baseName}-copy-${copyNum}`\n }\n return `${value}-copy`\n },\n ],\n },\n },\n\n // Page Layout\n createPageLayoutField(layouts, true),\n\n // Editor Version\n createEditorVersionField('puck', true),\n\n // Homepage Flag\n isHomepageField,\n\n // Puck Data (hidden - managed via visual editor)\n puckDataField,\n\n // SEO Fields\n seoFieldGroup,\n\n // Conversion Tracking Fields\n conversionFieldGroup,\n\n // Additional fields from options\n ...additionalFields,\n ]\n\n return {\n slug,\n admin: {\n useAsTitle: 'title',\n group: 'Content',\n defaultColumns: ['title', 'slug', '_status', 'updatedAt'],\n ...(collectionOverrides.admin ?? {}),\n },\n access: {\n read: access.read ?? defaultAccess,\n create: access.create ?? defaultAccess,\n update: access.update ?? defaultAccess,\n delete: access.delete ?? defaultAccess,\n ...(collectionOverrides.access ?? {}),\n },\n versions:\n typeof collectionOverrides.versions === 'object'\n ? { drafts: true, ...collectionOverrides.versions }\n : { drafts: true },\n fields: baseFields,\n ...collectionOverrides,\n // Ensure fields aren't overwritten by collectionOverrides\n ...(collectionOverrides.fields && {\n fields: [...baseFields, ...collectionOverrides.fields],\n }),\n }\n}\n\n// Note: puckDataField is now exported from '../fields' for hybrid collection integration\n// Re-export for backwards compatibility\nexport { puckDataField } from '../fields'\n","import type { CollectionConfig } from 'payload'\n\n/**\n * Templates Collection - Stores reusable Puck component configurations\n *\n * This collection stores serialized Puck component data that can be\n * loaded into Template components for reuse across pages.\n */\nexport const TemplatesCollection: CollectionConfig = {\n slug: 'puck-templates',\n admin: {\n useAsTitle: 'name',\n group: 'Puck',\n defaultColumns: ['name', 'category', 'updatedAt'],\n description: 'Reusable component templates for the visual editor',\n },\n access: {\n read: () => true,\n create: ({ req }) => !!req.user,\n update: ({ req }) => !!req.user,\n delete: ({ req }) => !!req.user,\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n required: true,\n admin: {\n description: 'A descriptive name for this template',\n },\n },\n {\n name: 'description',\n type: 'textarea',\n admin: {\n description: 'Optional description of what this template contains',\n },\n },\n {\n name: 'category',\n type: 'text',\n admin: {\n description: 'Category for organizing templates (e.g., \"Hero\", \"Footer\", \"CTA\")',\n },\n },\n {\n name: 'content',\n type: 'json',\n required: true,\n admin: {\n description: 'Serialized Puck component data',\n },\n },\n {\n name: 'thumbnail',\n type: 'text',\n admin: {\n description: 'Optional thumbnail URL for template preview',\n },\n },\n ],\n timestamps: true,\n}\n","import type { CollectionConfig, Config as PayloadConfig, Field, Plugin } from 'payload'\nimport type { PuckPluginOptions, PuckAdminConfig } from '../types'\nimport { generatePagesCollection } from './collections/Pages'\nimport { TemplatesCollection } from '../collections/Templates'\nimport { getPuckFields } from './fields'\n\n/**\n * Get all field names from a collection's fields array (including nested group fields and tabs)\n */\nfunction getExistingFieldNames(fields: Field[]): Set<string> {\n const names = new Set<string>()\n\n function addFieldNames(fieldsToCheck: Field[]) {\n for (const field of fieldsToCheck) {\n // Add the field name if it has one\n if ('name' in field && field.name) {\n names.add(field.name)\n }\n\n // Check nested fields in groups, rows, collapsibles, etc.\n if ('fields' in field && Array.isArray(field.fields)) {\n addFieldNames(field.fields)\n }\n\n // Check fields inside tabs\n if (field.type === 'tabs' && 'tabs' in field && Array.isArray(field.tabs)) {\n for (const tab of field.tabs) {\n if ('fields' in tab && Array.isArray(tab.fields)) {\n addFieldNames(tab.fields)\n }\n }\n }\n }\n }\n\n addFieldNames(fields)\n return names\n}\n\n/**\n * Filter out fields that already exist in the target collection\n */\nfunction filterExistingFields(fieldsToAdd: Field[], existingNames: Set<string>): Field[] {\n return fieldsToAdd.filter((field) => {\n if ('name' in field && field.name) {\n return !existingNames.has(field.name)\n }\n return true // Keep fields without names (like UI fields)\n })\n}\n\n/**\n * Generates the UI field configuration for the Edit with Puck button\n */\nfunction generatePuckEditField(\n collectionSlug: string,\n adminConfig: PuckAdminConfig = {}\n): Field {\n const {\n editorPathPattern = '/pages/{id}/edit',\n buttonLabel = 'Edit with Puck',\n buttonPosition, // undefined = main area (default), 'sidebar' = sidebar\n } = adminConfig\n\n return {\n name: 'puckEdit',\n type: 'ui',\n admin: {\n // Only set position if explicitly specified (sidebar)\n // undefined means main form area in Payload\n ...(buttonPosition && { position: buttonPosition }),\n components: {\n Field: '@delmaredigital/payload-puck/admin/client#EditWithPuckButton',\n Cell: '@delmaredigital/payload-puck/admin/client#EditWithPuckCell',\n },\n custom: {\n collectionSlug,\n editorPathPattern,\n label: buttonLabel,\n },\n },\n }\n}\n\n/**\n * Creates a Payload plugin that integrates Puck visual page builder\n *\n * This plugin:\n * - Generates a Pages collection with puckData field\n * - Adds an \"Edit with Puck\" button in the admin document view\n *\n * The Puck editor itself runs outside of Payload admin. Create your own\n * editor page (e.g., /pages/[id]/edit) using the PuckEditor component.\n *\n * @example\n * ```typescript\n * import { createPuckPlugin } from '@delmaredigital/payload-puck/plugin'\n *\n * export default buildConfig({\n * plugins: [\n * createPuckPlugin({\n * pagesCollection: 'pages',\n * access: {\n * read: () => true,\n * create: ({ req }) => !!req.user,\n * update: ({ req }) => !!req.user,\n * delete: ({ req }) => req.user?.role === 'admin',\n * },\n * admin: {\n * editorPathPattern: '/pages/{id}/edit',\n * buttonLabel: 'Visual Editor',\n * },\n * }),\n * ],\n * })\n * ```\n */\nexport function createPuckPlugin(options: PuckPluginOptions = {}): Plugin {\n const {\n pagesCollection = 'pages',\n autoGenerateCollection = true,\n admin: adminConfig = {},\n } = options\n\n const { addEditButton = true } = adminConfig\n\n return (incomingConfig: PayloadConfig): PayloadConfig => {\n // Generate Pages collection if auto-generate is enabled\n let collections = incomingConfig.collections || []\n\n // Always add the Templates collection if it doesn't exist\n const templatesCollectionExists = collections.some(\n (c) => c.slug === 'puck-templates'\n )\n if (!templatesCollectionExists) {\n collections = [...collections, TemplatesCollection]\n }\n\n if (autoGenerateCollection) {\n // Check if collection already exists\n const existingCollectionIndex = collections.findIndex(\n (c) => c.slug === pagesCollection\n )\n\n // Generate the edit button field if enabled\n const editButtonField = addEditButton\n ? [generatePuckEditField(pagesCollection, adminConfig)]\n : []\n\n if (existingCollectionIndex >= 0) {\n // Collection exists - only add Puck fields that don't already exist\n const existingCollection = collections[existingCollectionIndex]\n const existingFields = existingCollection.fields || []\n const existingFieldNames = getExistingFieldNames(existingFields)\n\n // Get Puck-specific fields (not the full collection with title/slug)\n // This avoids duplicating fields the user may have already defined\n const puckFields = getPuckFields({\n includeSEO: !existingFieldNames.has('meta'),\n includeConversion: !existingFieldNames.has('conversionTracking'),\n includeEditorVersion: !existingFieldNames.has('editorVersion'),\n includePageLayout: !existingFieldNames.has('pageLayout'),\n includeIsHomepage: !existingFieldNames.has('isHomepage'),\n layouts: options.layouts,\n })\n\n // Filter out any remaining duplicates (e.g., puckData if user already has it)\n const fieldsToAdd = filterExistingFields(puckFields, existingFieldNames)\n\n // Only add edit button if puckEdit doesn't exist\n const editFieldsToAdd = existingFieldNames.has('puckEdit') ? [] : editButtonField\n\n collections = [\n ...collections.slice(0, existingCollectionIndex),\n {\n ...existingCollection,\n // Ensure drafts are enabled for Puck\n versions:\n typeof existingCollection.versions === 'object'\n ? { drafts: true, ...existingCollection.versions }\n : existingCollection.versions ?? { drafts: true },\n fields: [\n ...existingFields,\n ...fieldsToAdd,\n ...editFieldsToAdd,\n ],\n },\n ...collections.slice(existingCollectionIndex + 1),\n ]\n } else {\n // Add new collection with edit button field\n const generatedCollection = generatePagesCollection(pagesCollection, options)\n collections = [\n ...collections,\n {\n ...generatedCollection,\n fields: [...generatedCollection.fields, ...editButtonField],\n },\n ]\n }\n }\n\n return {\n ...incomingConfig,\n collections,\n onInit: async (payload) => {\n // Call existing onInit if present\n if (incomingConfig.onInit) {\n await incomingConfig.onInit(payload)\n }\n },\n }\n }\n}\n\n// Re-export collection utilities\nexport { generatePagesCollection } from './collections/Pages'\nexport { TemplatesCollection } from '../collections/Templates'\n\n// Re-export field utilities for hybrid collection integration\nexport {\n getPuckFields,\n puckDataField,\n editorVersionField,\n createEditorVersionField,\n pageLayoutField,\n createPageLayoutField,\n isHomepageField,\n seoFieldGroup,\n conversionFieldGroup,\n} from './fields'\n\n// Export the edit button generator for hybrid collections\nexport { generatePuckEditField }\n\n// Re-export types\nexport type { PuckPluginOptions, PuckAdminConfig } from '../types'\nexport type { GetPuckFieldsOptions } from './fields/types'\n"]}
|