@riverbankcms/sdk 0.2.0 → 0.3.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/cli/index.js +4840 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/client/bookings.d.mts +82 -2
- package/dist/client/bookings.d.ts +82 -2
- package/dist/client/bookings.js +1623 -3
- package/dist/client/bookings.js.map +1 -1
- package/dist/client/bookings.mjs +1610 -5
- package/dist/client/bookings.mjs.map +1 -1
- package/dist/client/client.d.mts +8 -5
- package/dist/client/client.d.ts +8 -5
- package/dist/client/client.js +16873 -322
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +16855 -307
- package/dist/client/client.mjs.map +1 -1
- package/dist/client/hooks.d.mts +10 -7
- package/dist/client/hooks.d.ts +10 -7
- package/dist/client/hooks.js +5074 -4
- package/dist/client/hooks.js.map +1 -1
- package/dist/client/hooks.mjs +5074 -4
- package/dist/client/hooks.mjs.map +1 -1
- package/dist/client/rendering/client.d.mts +7 -1
- package/dist/client/rendering/client.d.ts +7 -1
- package/dist/client/rendering/client.js +17388 -2
- package/dist/client/rendering/client.js.map +1 -1
- package/dist/client/rendering/client.mjs +17382 -2
- package/dist/client/rendering/client.mjs.map +1 -1
- package/dist/client/resolver-BhueZVxZ.d.mts +61 -0
- package/dist/client/resolver-BhueZVxZ.d.ts +61 -0
- package/dist/client/usePage-BBcFCxOU.d.ts +6297 -0
- package/dist/client/usePage-BydHcMYB.d.mts +6297 -0
- package/dist/client/usePage-CrKw1H6Y.d.ts +6338 -0
- package/dist/client/usePage-DMI8ImsU.d.mts +6338 -0
- package/dist/server/Layout-BM_KmCxO.d.ts +44 -0
- package/dist/server/Layout-CsAQ-0Fv.d.mts +44 -0
- package/dist/server/{chunk-JB4LIEFS.js → chunk-5R4NMVXA.js} +15 -8
- package/dist/server/chunk-5R4NMVXA.js.map +1 -0
- package/dist/server/{chunk-ADREPXFU.js → chunk-62ZJI564.js} +3 -3
- package/dist/server/{chunk-ADREPXFU.js.map → chunk-62ZJI564.js.map} +1 -1
- package/dist/server/chunk-7DS4Q3GA.mjs +333 -0
- package/dist/server/chunk-7DS4Q3GA.mjs.map +1 -0
- package/dist/server/chunk-BJTO5JO5.mjs +11 -0
- package/dist/server/{chunk-4Z5FBFRL.mjs → chunk-BPKYRPCQ.mjs} +7 -3
- package/dist/server/{chunk-4Z5FBFRL.mjs.map → chunk-BPKYRPCQ.mjs.map} +1 -1
- package/dist/server/chunk-DGUM43GV.js +11 -0
- package/dist/server/chunk-DGUM43GV.js.map +1 -0
- package/dist/server/chunk-EGTDJ4PL.js +5461 -0
- package/dist/server/chunk-EGTDJ4PL.js.map +1 -0
- package/dist/server/chunk-FK64TZBT.mjs +831 -0
- package/dist/server/chunk-FK64TZBT.mjs.map +1 -0
- package/dist/server/chunk-HOY77YBF.js +333 -0
- package/dist/server/chunk-HOY77YBF.js.map +1 -0
- package/dist/server/chunk-INWKF3IC.js +831 -0
- package/dist/server/chunk-INWKF3IC.js.map +1 -0
- package/dist/server/{chunk-2RW5HAQQ.mjs → chunk-JTAERCX2.mjs} +2 -2
- package/dist/server/chunk-O5DC7MYW.mjs +9606 -0
- package/dist/server/chunk-O5DC7MYW.mjs.map +1 -0
- package/dist/server/{chunk-PEAXKTDU.mjs → chunk-OP2GHK27.mjs} +2 -2
- package/dist/server/{chunk-WKG57P2H.mjs → chunk-PN3CHDVX.mjs} +10 -3
- package/dist/server/{chunk-WKG57P2H.mjs.map → chunk-PN3CHDVX.mjs.map} +1 -1
- package/dist/server/chunk-QFFQTOY3.mjs +2128 -0
- package/dist/server/chunk-QFFQTOY3.mjs.map +1 -0
- package/dist/server/chunk-SF63XAX7.js +9606 -0
- package/dist/server/chunk-SF63XAX7.js.map +1 -0
- package/dist/server/{chunk-F472SMKX.js → chunk-TO7FD6TQ.js} +4 -4
- package/dist/server/{chunk-F472SMKX.js.map → chunk-TO7FD6TQ.js.map} +1 -1
- package/dist/server/chunk-UFVCBGBY.js +2128 -0
- package/dist/server/chunk-UFVCBGBY.js.map +1 -0
- package/dist/server/chunk-USQF2XTU.mjs +5461 -0
- package/dist/server/chunk-USQF2XTU.mjs.map +1 -0
- package/dist/server/{chunk-SW7LE4M3.js → chunk-XLVL5WPH.js} +12 -8
- package/dist/server/chunk-XLVL5WPH.js.map +1 -0
- package/dist/server/components-CI3JiOYA.d.mts +305 -0
- package/dist/server/components-DJBLu_yc.d.ts +305 -0
- package/dist/server/components.d.mts +14 -49
- package/dist/server/components.d.ts +14 -49
- package/dist/server/components.js +7 -4
- package/dist/server/components.js.map +1 -1
- package/dist/server/components.mjs +9 -6
- package/dist/server/components.mjs.map +1 -1
- package/dist/server/config-validation.d.mts +2 -2
- package/dist/server/config-validation.d.ts +2 -2
- package/dist/server/config-validation.js +6 -3
- package/dist/server/config-validation.js.map +1 -1
- package/dist/server/config-validation.mjs +5 -2
- package/dist/server/config.d.mts +3 -3
- package/dist/server/config.d.ts +3 -3
- package/dist/server/config.js +6 -3
- package/dist/server/config.js.map +1 -1
- package/dist/server/config.mjs +5 -2
- package/dist/server/config.mjs.map +1 -1
- package/dist/server/data.d.mts +10 -8
- package/dist/server/data.d.ts +10 -8
- package/dist/server/data.js +4 -2
- package/dist/server/data.js.map +1 -1
- package/dist/server/data.mjs +3 -1
- package/dist/server/{index-B0yI_V6Z.d.mts → index-DoX3ELQn.d.mts} +1 -1
- package/dist/server/{index-C6M0Wfjq.d.ts → index-Dus2gkY6.d.ts} +1 -1
- package/dist/server/index.d.mts +1568 -5
- package/dist/server/index.d.ts +1568 -5
- package/dist/server/index.js +4 -4
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +4 -4
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/{loadContent-CJcbYF3J.d.ts → loadContent-CdXDGsJM.d.ts} +5 -4
- package/dist/server/{loadContent-zhlL4YSE.d.mts → loadContent-v2n6pOlO.d.mts} +5 -4
- package/dist/server/loadPage-3ECPF426.js +11 -0
- package/dist/server/loadPage-3ECPF426.js.map +1 -0
- package/dist/server/loadPage-LW273NYO.mjs +11 -0
- package/dist/server/loadPage-LW273NYO.mjs.map +1 -0
- package/dist/server/{loadPage-CCf15nt8.d.mts → loadPage-bejlajm9.d.ts} +147 -5
- package/dist/server/{loadPage-BYmVMk0V.d.ts → loadPage-en10WQrt.d.mts} +147 -5
- package/dist/server/metadata.d.mts +10 -6
- package/dist/server/metadata.d.ts +10 -6
- package/dist/server/metadata.js +3 -1
- package/dist/server/metadata.js.map +1 -1
- package/dist/server/metadata.mjs +2 -0
- package/dist/server/metadata.mjs.map +1 -1
- package/dist/server/navigation.d.mts +100 -0
- package/dist/server/navigation.d.ts +100 -0
- package/dist/server/navigation.js +44 -0
- package/dist/server/navigation.js.map +1 -0
- package/dist/server/navigation.mjs +44 -0
- package/dist/server/navigation.mjs.map +1 -0
- package/dist/server/rendering/server.d.mts +10 -7
- package/dist/server/rendering/server.d.ts +10 -7
- package/dist/server/rendering/server.js +7 -4
- package/dist/server/rendering/server.js.map +1 -1
- package/dist/server/rendering/server.mjs +6 -3
- package/dist/server/rendering.d.mts +173 -9
- package/dist/server/rendering.d.ts +173 -9
- package/dist/server/rendering.js +12 -9
- package/dist/server/rendering.js.map +1 -1
- package/dist/server/rendering.mjs +14 -11
- package/dist/server/rendering.mjs.map +1 -1
- package/dist/server/routing.d.mts +10 -6
- package/dist/server/routing.d.ts +10 -6
- package/dist/server/routing.js +4 -2
- package/dist/server/routing.js.map +1 -1
- package/dist/server/routing.mjs +3 -1
- package/dist/server/routing.mjs.map +1 -1
- package/dist/server/schema-Bpy9N5ZI.d.mts +1870 -0
- package/dist/server/schema-Bpy9N5ZI.d.ts +1870 -0
- package/dist/server/server.d.mts +12 -8
- package/dist/server/server.d.ts +12 -8
- package/dist/server/server.js +7 -5
- package/dist/server/server.js.map +1 -1
- package/dist/server/server.mjs +6 -4
- package/dist/server/theme-bridge.js +13 -10
- package/dist/server/theme-bridge.js.map +1 -1
- package/dist/server/theme-bridge.mjs +10 -7
- package/dist/server/theme-bridge.mjs.map +1 -1
- package/dist/server/theme.js +3 -1
- package/dist/server/theme.js.map +1 -1
- package/dist/server/theme.mjs +2 -0
- package/dist/server/theme.mjs.map +1 -1
- package/dist/server/{types-BCeqWtI2.d.mts → types-Bq3520hK.d.mts} +3 -3
- package/dist/server/{types-C6gmRHLe.d.mts → types-CLusapsM.d.mts} +1 -1
- package/dist/server/types-Cc7lyPkN.d.ts +4043 -0
- package/dist/server/{types-BCeqWtI2.d.ts → types-D-rqOU5I.d.ts} +3 -3
- package/dist/server/{types-C6gmRHLe.d.ts → types-Ls6BkLKg.d.ts} +1 -1
- package/dist/server/{types-Bbo01M7P.d.mts → types-_nDnPHpv.d.mts} +27 -1
- package/dist/server/{types-Bbo01M7P.d.ts → types-_nDnPHpv.d.ts} +27 -1
- package/dist/server/types-nVerjjdv.d.mts +4043 -0
- package/package.json +22 -17
- package/dist/server/chunk-3KKZVGH4.mjs +0 -179
- package/dist/server/chunk-3KKZVGH4.mjs.map +0 -1
- package/dist/server/chunk-4Z3GPTCS.js +0 -179
- package/dist/server/chunk-4Z3GPTCS.js.map +0 -1
- package/dist/server/chunk-JB4LIEFS.js.map +0 -1
- package/dist/server/chunk-QQ6U4QX6.js +0 -120
- package/dist/server/chunk-QQ6U4QX6.js.map +0 -1
- package/dist/server/chunk-R5YGLRUG.mjs +0 -122
- package/dist/server/chunk-R5YGLRUG.mjs.map +0 -1
- package/dist/server/chunk-SW7LE4M3.js.map +0 -1
- package/dist/server/chunk-W3K7LVPS.mjs +0 -120
- package/dist/server/chunk-W3K7LVPS.mjs.map +0 -1
- package/dist/server/chunk-YHEZMVTS.js +0 -122
- package/dist/server/chunk-YHEZMVTS.js.map +0 -1
- package/dist/server/loadPage-DVH3DW6E.js +0 -9
- package/dist/server/loadPage-DVH3DW6E.js.map +0 -1
- package/dist/server/loadPage-PHQZ6XQZ.mjs +0 -9
- /package/dist/server/{loadPage-PHQZ6XQZ.mjs.map → chunk-BJTO5JO5.mjs.map} +0 -0
- /package/dist/server/{chunk-2RW5HAQQ.mjs.map → chunk-JTAERCX2.mjs.map} +0 -0
- /package/dist/server/{chunk-PEAXKTDU.mjs.map → chunk-OP2GHK27.mjs.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -3,13 +3,4844 @@
|
|
|
3
3
|
|
|
4
4
|
var commander = require('commander');
|
|
5
5
|
var zod = require('zod');
|
|
6
|
-
var blocks = require('@riverbankcms/blocks');
|
|
7
|
-
require('@riverbankcms/blocks/system/data');
|
|
8
6
|
var jiti = require('jiti');
|
|
9
7
|
var path = require('path');
|
|
10
8
|
var fs = require('fs');
|
|
11
9
|
|
|
12
10
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
11
|
+
// ../blocks/src/system/manifest/augmentManifest.ts
|
|
12
|
+
function augmentManifest(manifest) {
|
|
13
|
+
let augmentedFields = manifest.fields ?? [];
|
|
14
|
+
const variantField = createVariantField(manifest);
|
|
15
|
+
if (variantField) {
|
|
16
|
+
augmentedFields = [variantField, ...augmentedFields];
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
...manifest,
|
|
20
|
+
fields: augmentedFields
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function createVariantField(manifest) {
|
|
24
|
+
if (!manifest.variants || Object.keys(manifest.variants).length <= 1) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const variantKeys = Object.keys(manifest.variants);
|
|
28
|
+
const field = {
|
|
29
|
+
id: "variant",
|
|
30
|
+
type: "select",
|
|
31
|
+
label: "Variant",
|
|
32
|
+
description: "Choose a layout variant for this block",
|
|
33
|
+
required: false,
|
|
34
|
+
defaultValue: manifest.defaultVariant ?? variantKeys[0],
|
|
35
|
+
options: variantKeys.map((key) => ({
|
|
36
|
+
value: key,
|
|
37
|
+
label: formatVariantLabel(key)
|
|
38
|
+
})),
|
|
39
|
+
multiple: false
|
|
40
|
+
};
|
|
41
|
+
return field;
|
|
42
|
+
}
|
|
43
|
+
function formatVariantLabel(variantKey) {
|
|
44
|
+
return variantKey.replace(/([A-Z])/g, " $1").replace(/_/g, " ").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ../blocks/src/system/manifest/registry.ts
|
|
48
|
+
var REGISTRY_SYMBOL = Symbol.for("@riverbankcms/blocks/manifest-registry");
|
|
49
|
+
var globalScope = globalThis;
|
|
50
|
+
if (!globalScope[REGISTRY_SYMBOL]) {
|
|
51
|
+
globalScope[REGISTRY_SYMBOL] = /* @__PURE__ */ new Map();
|
|
52
|
+
}
|
|
53
|
+
var manifestStore = globalScope[REGISTRY_SYMBOL];
|
|
54
|
+
function registerManifest(manifest) {
|
|
55
|
+
manifestStore.set(manifest.name, manifest);
|
|
56
|
+
return manifest;
|
|
57
|
+
}
|
|
58
|
+
var transformStepSchema = zod.z.object({
|
|
59
|
+
id: zod.z.string().min(1, "Transform requires an identifier"),
|
|
60
|
+
options: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
61
|
+
});
|
|
62
|
+
var bindingPathSchema = zod.z.string().min(1, "Binding path is required").regex(/[A-Za-z0-9_\.$\[\]/-]+/, "Binding path contains invalid characters");
|
|
63
|
+
var bindingSchema = zod.z.object({
|
|
64
|
+
from: bindingPathSchema,
|
|
65
|
+
fallback: zod.z.unknown().optional(),
|
|
66
|
+
transforms: transformStepSchema.array().default([]),
|
|
67
|
+
pick: zod.z.enum(["value", "collection", "context"]).default("value")
|
|
68
|
+
});
|
|
69
|
+
var repeatSchema = zod.z.object({
|
|
70
|
+
collection: bindingSchema,
|
|
71
|
+
itemName: zod.z.string().min(1).default("item"),
|
|
72
|
+
indexName: zod.z.string().min(1).default("index"),
|
|
73
|
+
limit: zod.z.number().int().positive().optional(),
|
|
74
|
+
sortBy: zod.z.object({
|
|
75
|
+
path: bindingPathSchema,
|
|
76
|
+
direction: zod.z.enum(["asc", "desc"]).default("asc")
|
|
77
|
+
}).optional()
|
|
78
|
+
});
|
|
79
|
+
var conditionSchema = zod.z.object({
|
|
80
|
+
when: bindingSchema,
|
|
81
|
+
equals: zod.z.unknown().optional(),
|
|
82
|
+
not: zod.z.boolean().default(false)
|
|
83
|
+
});
|
|
84
|
+
var dataScopeSchema = zod.z.object({
|
|
85
|
+
name: zod.z.string().min(1),
|
|
86
|
+
from: bindingSchema
|
|
87
|
+
});
|
|
88
|
+
var nodePropsSchema = zod.z.record(zod.z.string(), zod.z.unknown()).default({});
|
|
89
|
+
var nodeSchema = zod.z.object({
|
|
90
|
+
type: zod.z.string().min(1, "Node type is required"),
|
|
91
|
+
key: zod.z.string().optional(),
|
|
92
|
+
props: nodePropsSchema.optional(),
|
|
93
|
+
children: zod.z.lazy(() => nodeSchema.array().default([])).optional(),
|
|
94
|
+
$bind: bindingSchema.optional(),
|
|
95
|
+
$repeat: repeatSchema.optional(),
|
|
96
|
+
$when: conditionSchema.optional(),
|
|
97
|
+
$scopes: dataScopeSchema.array().optional()
|
|
98
|
+
});
|
|
99
|
+
nodeSchema.array().or(nodeSchema);
|
|
100
|
+
var NodeSchema = nodeSchema;
|
|
101
|
+
|
|
102
|
+
// ../blocks/src/system/node/typeBasedLayout.ts
|
|
103
|
+
function typeBasedLayout(itemTypesMap, options) {
|
|
104
|
+
const itemName = options?.itemName ?? "item";
|
|
105
|
+
const result = Object.entries(itemTypesMap).map(([typeId, layout]) => {
|
|
106
|
+
const node = Array.isArray(layout) && layout.length === 1 ? layout[0] : layout;
|
|
107
|
+
return {
|
|
108
|
+
...node,
|
|
109
|
+
$when: {
|
|
110
|
+
when: { from: `${itemName}._type` },
|
|
111
|
+
equals: typeId
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ../blocks/src/system/manifest/schema.ts
|
|
119
|
+
var visibilityLevels = ["admin", "designer", "author"];
|
|
120
|
+
var uiSchema = zod.z.object({
|
|
121
|
+
widget: zod.z.string().optional(),
|
|
122
|
+
flattenInRepeater: zod.z.boolean().optional(),
|
|
123
|
+
hidden: zod.z.boolean().optional(),
|
|
124
|
+
hideLabel: zod.z.boolean().optional(),
|
|
125
|
+
hideDescription: zod.z.boolean().optional(),
|
|
126
|
+
showCharCount: zod.z.union([zod.z.number().int().positive(), zod.z.object({ max: zod.z.number().int().positive() })]).optional(),
|
|
127
|
+
showSlugPreview: zod.z.boolean().optional(),
|
|
128
|
+
variant: zod.z.enum(["full", "inline", "limited", "media"]).optional(),
|
|
129
|
+
richTextVariant: zod.z.enum(["full", "inline", "limited", "media"]).optional(),
|
|
130
|
+
// Optional input hints for validators/widgets
|
|
131
|
+
inputType: zod.z.enum(["text", "email", "tel", "number"]).optional(),
|
|
132
|
+
min: zod.z.number().optional(),
|
|
133
|
+
max: zod.z.number().optional(),
|
|
134
|
+
step: zod.z.number().optional(),
|
|
135
|
+
pattern: zod.z.string().optional(),
|
|
136
|
+
placeholder: zod.z.string().optional(),
|
|
137
|
+
visibleWhen: zod.z.object({
|
|
138
|
+
field: zod.z.string().min(1),
|
|
139
|
+
equals: zod.z.any().optional(),
|
|
140
|
+
notEquals: zod.z.any().optional(),
|
|
141
|
+
oneOf: zod.z.array(zod.z.any()).optional(),
|
|
142
|
+
notIn: zod.z.array(zod.z.any()).optional()
|
|
143
|
+
}).optional(),
|
|
144
|
+
// Modal configuration for modal and group fields
|
|
145
|
+
modalConfig: zod.z.object({
|
|
146
|
+
buttonLabel: zod.z.string().optional(),
|
|
147
|
+
description: zod.z.string().optional(),
|
|
148
|
+
buttonVariant: zod.z.enum(["default", "outline"]).optional(),
|
|
149
|
+
showCustomizedIndicator: zod.z.boolean().optional(),
|
|
150
|
+
maxWidth: zod.z.string().optional()
|
|
151
|
+
}).optional(),
|
|
152
|
+
// Background field configuration
|
|
153
|
+
allowedTypes: zod.z.array(zod.z.enum(["color", "gradient", "image"])).optional(),
|
|
154
|
+
// Tab group configuration
|
|
155
|
+
fullWidth: zod.z.boolean().optional(),
|
|
156
|
+
// Field layout configuration
|
|
157
|
+
row: zod.z.string().optional(),
|
|
158
|
+
colSpan: zod.z.number().int().min(1).max(4).optional(),
|
|
159
|
+
// Group layout configuration
|
|
160
|
+
layout: zod.z.enum(["stack", "grid"]).optional(),
|
|
161
|
+
columns: zod.z.number().int().min(2).max(4).optional(),
|
|
162
|
+
// Entry picker configuration
|
|
163
|
+
contentTypeField: zod.z.string().optional()
|
|
164
|
+
}).partial();
|
|
165
|
+
var baseFieldSchema = zod.z.object({
|
|
166
|
+
id: zod.z.string().min(1, "Field id is required"),
|
|
167
|
+
label: zod.z.string().min(1, "Field label is required"),
|
|
168
|
+
description: zod.z.string().optional(),
|
|
169
|
+
visibleRoles: zod.z.array(zod.z.enum(visibilityLevels)).optional(),
|
|
170
|
+
defaultValue: zod.z.any().optional(),
|
|
171
|
+
required: zod.z.boolean().default(false),
|
|
172
|
+
ui: uiSchema.optional()
|
|
173
|
+
});
|
|
174
|
+
var textFieldSchema = baseFieldSchema.extend({
|
|
175
|
+
type: zod.z.literal("text"),
|
|
176
|
+
multiline: zod.z.boolean().default(false),
|
|
177
|
+
maxLength: zod.z.number().int().positive().optional()
|
|
178
|
+
});
|
|
179
|
+
var richTextFieldSchema = baseFieldSchema.extend({
|
|
180
|
+
type: zod.z.literal("richText"),
|
|
181
|
+
format: zod.z.enum(["markdown", "html"]).default("markdown")
|
|
182
|
+
});
|
|
183
|
+
var mediaFieldSchema = baseFieldSchema.extend({
|
|
184
|
+
type: zod.z.literal("media"),
|
|
185
|
+
mediaKinds: zod.z.array(zod.z.enum(["image", "video"])).default(["image"]),
|
|
186
|
+
aspectRatio: zod.z.string().optional()
|
|
187
|
+
});
|
|
188
|
+
var booleanFieldSchema = baseFieldSchema.extend({
|
|
189
|
+
type: zod.z.literal("boolean")
|
|
190
|
+
});
|
|
191
|
+
var dateFieldSchema = baseFieldSchema.extend({
|
|
192
|
+
type: zod.z.literal("date")
|
|
193
|
+
});
|
|
194
|
+
var timeFieldSchema = baseFieldSchema.extend({
|
|
195
|
+
type: zod.z.literal("time")
|
|
196
|
+
});
|
|
197
|
+
var dateTimeFieldSchema = baseFieldSchema.extend({
|
|
198
|
+
type: zod.z.literal("datetime")
|
|
199
|
+
});
|
|
200
|
+
var slugFieldSchema = baseFieldSchema.extend({
|
|
201
|
+
type: zod.z.literal("slug"),
|
|
202
|
+
sourceFieldId: zod.z.string().min(1).optional(),
|
|
203
|
+
statusFieldId: zod.z.string().min(1).optional(),
|
|
204
|
+
maxLength: zod.z.number().int().positive().optional()
|
|
205
|
+
});
|
|
206
|
+
var urlFieldSchema = baseFieldSchema.extend({
|
|
207
|
+
type: zod.z.literal("url"),
|
|
208
|
+
allowRelative: zod.z.boolean().default(false)
|
|
209
|
+
});
|
|
210
|
+
var linkFieldSchema = baseFieldSchema.extend({
|
|
211
|
+
type: zod.z.literal("link")
|
|
212
|
+
});
|
|
213
|
+
var selectFieldSchema = baseFieldSchema.extend({
|
|
214
|
+
type: zod.z.literal("select"),
|
|
215
|
+
options: zod.z.array(zod.z.object({
|
|
216
|
+
value: zod.z.string(),
|
|
217
|
+
label: zod.z.string()
|
|
218
|
+
})).min(1),
|
|
219
|
+
multiple: zod.z.boolean().default(false)
|
|
220
|
+
});
|
|
221
|
+
var referenceFieldSchema = baseFieldSchema.extend({
|
|
222
|
+
type: zod.z.literal("reference"),
|
|
223
|
+
referenceKind: zod.z.string().min(1),
|
|
224
|
+
allowManualEntry: zod.z.boolean().default(false)
|
|
225
|
+
});
|
|
226
|
+
var itemTypeSchema = zod.z.object({
|
|
227
|
+
label: zod.z.string().min(1),
|
|
228
|
+
icon: zod.z.string().optional(),
|
|
229
|
+
fields: zod.z.lazy(() => getFieldSchemaInternal().array().min(1, "Item type requires at least one field"))
|
|
230
|
+
});
|
|
231
|
+
var repeaterFieldSchema = baseFieldSchema.extend({
|
|
232
|
+
type: zod.z.literal("repeater"),
|
|
233
|
+
itemLabel: zod.z.string().default("Item"),
|
|
234
|
+
itemLabelSource: zod.z.string().optional(),
|
|
235
|
+
minItems: zod.z.number().int().min(0).default(0),
|
|
236
|
+
maxItems: zod.z.number().int().positive().optional(),
|
|
237
|
+
// Monomorphic mode
|
|
238
|
+
schema: zod.z.object({
|
|
239
|
+
fields: zod.z.lazy(() => getFieldSchemaInternal().array().min(1, "Repeater requires at least one field"))
|
|
240
|
+
}).optional(),
|
|
241
|
+
// Polymorphic mode
|
|
242
|
+
polymorphic: zod.z.boolean().optional(),
|
|
243
|
+
itemTypes: zod.z.record(zod.z.string(), itemTypeSchema).optional(),
|
|
244
|
+
allowConversion: zod.z.boolean().default(true)
|
|
245
|
+
}).refine(
|
|
246
|
+
(data) => {
|
|
247
|
+
const hasSchema = data.schema !== void 0;
|
|
248
|
+
const hasPolymorphic = data.polymorphic === true && data.itemTypes !== void 0;
|
|
249
|
+
return hasSchema !== hasPolymorphic;
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
message: "Repeater must have either 'schema' (monomorphic) or 'polymorphic: true' with 'itemTypes' (polymorphic)"
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
var groupFieldSchema = baseFieldSchema.extend({
|
|
256
|
+
type: zod.z.literal("group"),
|
|
257
|
+
schema: zod.z.object({
|
|
258
|
+
fields: zod.z.lazy(() => getFieldSchemaInternal().array().min(1, "Group requires at least one field"))
|
|
259
|
+
})
|
|
260
|
+
});
|
|
261
|
+
var modalFieldSchema = baseFieldSchema.extend({
|
|
262
|
+
type: zod.z.literal("modal"),
|
|
263
|
+
schema: zod.z.object({
|
|
264
|
+
fields: zod.z.lazy(() => getFieldSchemaInternal().array().min(1, "Modal requires at least one field"))
|
|
265
|
+
})
|
|
266
|
+
});
|
|
267
|
+
var numberFieldSchema = baseFieldSchema.extend({
|
|
268
|
+
type: zod.z.literal("number"),
|
|
269
|
+
min: zod.z.number().optional(),
|
|
270
|
+
max: zod.z.number().optional(),
|
|
271
|
+
step: zod.z.number().optional()
|
|
272
|
+
});
|
|
273
|
+
var tabDefinitionSchema = zod.z.object({
|
|
274
|
+
id: zod.z.string().min(1),
|
|
275
|
+
label: zod.z.string().min(1),
|
|
276
|
+
icon: zod.z.string().optional(),
|
|
277
|
+
description: zod.z.string().optional(),
|
|
278
|
+
fields: zod.z.lazy(() => getFieldSchemaInternal().array()),
|
|
279
|
+
/** SDK section option that controls tab visibility based on site config */
|
|
280
|
+
sdkSectionOption: zod.z.enum(["backgroundColor", "backgroundGradient", "backgroundImage"]).optional()
|
|
281
|
+
});
|
|
282
|
+
var tabGroupFieldSchema = baseFieldSchema.extend({
|
|
283
|
+
type: zod.z.literal("tabGroup"),
|
|
284
|
+
tabs: zod.z.array(tabDefinitionSchema).min(1, "TabGroup requires at least one tab"),
|
|
285
|
+
activeTabField: zod.z.string().optional()
|
|
286
|
+
});
|
|
287
|
+
var presetOptionSchema = zod.z.object({
|
|
288
|
+
value: zod.z.string(),
|
|
289
|
+
label: zod.z.string()
|
|
290
|
+
});
|
|
291
|
+
var presetOrCustomFieldSchema = baseFieldSchema.extend({
|
|
292
|
+
type: zod.z.literal("presetOrCustom"),
|
|
293
|
+
presets: zod.z.array(presetOptionSchema).min(1, "PresetOrCustom requires at least one preset"),
|
|
294
|
+
customInput: zod.z.object({
|
|
295
|
+
placeholder: zod.z.string().optional(),
|
|
296
|
+
pattern: zod.z.string().optional(),
|
|
297
|
+
helpText: zod.z.string().optional()
|
|
298
|
+
}).optional()
|
|
299
|
+
});
|
|
300
|
+
var contentTypeSelectFieldSchema = baseFieldSchema.extend({
|
|
301
|
+
type: zod.z.literal("contentTypeSelect"),
|
|
302
|
+
/** Filter: all, routable (hasPages=true), nonRoutable (hasPages=false) */
|
|
303
|
+
filter: zod.z.enum(["all", "routable", "nonRoutable"]).default("all")
|
|
304
|
+
});
|
|
305
|
+
var entryPickerFieldSchema = baseFieldSchema.extend({
|
|
306
|
+
type: zod.z.literal("entryPicker")
|
|
307
|
+
});
|
|
308
|
+
var _fieldSchemaInternal = null;
|
|
309
|
+
function getFieldSchemaInternal() {
|
|
310
|
+
if (_fieldSchemaInternal) {
|
|
311
|
+
return _fieldSchemaInternal;
|
|
312
|
+
}
|
|
313
|
+
_fieldSchemaInternal = zod.z.discriminatedUnion("type", [
|
|
314
|
+
textFieldSchema,
|
|
315
|
+
richTextFieldSchema,
|
|
316
|
+
mediaFieldSchema,
|
|
317
|
+
booleanFieldSchema,
|
|
318
|
+
numberFieldSchema,
|
|
319
|
+
dateFieldSchema,
|
|
320
|
+
timeFieldSchema,
|
|
321
|
+
dateTimeFieldSchema,
|
|
322
|
+
slugFieldSchema,
|
|
323
|
+
urlFieldSchema,
|
|
324
|
+
linkFieldSchema,
|
|
325
|
+
selectFieldSchema,
|
|
326
|
+
referenceFieldSchema,
|
|
327
|
+
repeaterFieldSchema,
|
|
328
|
+
groupFieldSchema,
|
|
329
|
+
modalFieldSchema,
|
|
330
|
+
tabGroupFieldSchema,
|
|
331
|
+
presetOrCustomFieldSchema,
|
|
332
|
+
contentTypeSelectFieldSchema,
|
|
333
|
+
entryPickerFieldSchema
|
|
334
|
+
]);
|
|
335
|
+
return _fieldSchemaInternal;
|
|
336
|
+
}
|
|
337
|
+
var fieldSchema = new Proxy({}, {
|
|
338
|
+
get(_, prop) {
|
|
339
|
+
const schema = getFieldSchemaInternal();
|
|
340
|
+
const value = schema[prop];
|
|
341
|
+
return typeof value === "function" ? value.bind(schema) : value;
|
|
342
|
+
},
|
|
343
|
+
// Forward has checks to the real schema
|
|
344
|
+
has(_, prop) {
|
|
345
|
+
return prop in getFieldSchemaInternal();
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
var slotSchema = zod.z.object({
|
|
349
|
+
id: zod.z.string().min(1),
|
|
350
|
+
label: zod.z.string().min(1),
|
|
351
|
+
allowedKinds: zod.z.array(zod.z.string()).default([]),
|
|
352
|
+
min: zod.z.number().int().min(0).default(0),
|
|
353
|
+
max: zod.z.number().int().positive().optional(),
|
|
354
|
+
help: zod.z.string().optional(),
|
|
355
|
+
visibleRoles: zod.z.array(zod.z.enum(visibilityLevels)).optional()
|
|
356
|
+
});
|
|
357
|
+
var typographyTokens = ["display", "heading", "subheading", "body", "caption"];
|
|
358
|
+
var colorTokens = ["background", "surface", "foreground", "accent", "muted"];
|
|
359
|
+
var spacingTokens = ["none", "xs", "sm", "md", "lg", "xl"];
|
|
360
|
+
var radiusTokens = ["none", "sm", "md", "lg", "full"];
|
|
361
|
+
var styleTokenSchema = zod.z.object({
|
|
362
|
+
background: zod.z.enum(colorTokens).optional(),
|
|
363
|
+
foreground: zod.z.enum(colorTokens).optional(),
|
|
364
|
+
border: zod.z.enum(colorTokens).optional(),
|
|
365
|
+
typography: zod.z.enum(typographyTokens).optional(),
|
|
366
|
+
spacing: zod.z.enum(spacingTokens).optional(),
|
|
367
|
+
radius: zod.z.enum(radiusTokens).optional()
|
|
368
|
+
});
|
|
369
|
+
var behaviourSchema = zod.z.object({
|
|
370
|
+
supportsThemeSwitching: zod.z.boolean().default(true),
|
|
371
|
+
inlineEditing: zod.z.boolean().default(false),
|
|
372
|
+
animation: zod.z.boolean().default(false),
|
|
373
|
+
// Hide from block picker palettes (e.g., header/footer system blocks)
|
|
374
|
+
paletteHidden: zod.z.boolean().default(false)
|
|
375
|
+
});
|
|
376
|
+
var blockCategoryEnum = zod.z.enum(["marketing", "content", "blog", "media", "layout", "interactive"]);
|
|
377
|
+
zod.z.object({
|
|
378
|
+
name: zod.z.string().min(1),
|
|
379
|
+
version: zod.z.string().min(1),
|
|
380
|
+
title: zod.z.string().min(1),
|
|
381
|
+
titleSource: zod.z.string().optional(),
|
|
382
|
+
description: zod.z.string().optional(),
|
|
383
|
+
component: zod.z.string().min(1),
|
|
384
|
+
fields: fieldSchema.array().default([]),
|
|
385
|
+
slots: slotSchema.array().default([]),
|
|
386
|
+
styleTokens: styleTokenSchema.optional(),
|
|
387
|
+
behaviours: behaviourSchema.optional(),
|
|
388
|
+
layout: NodeSchema.optional(),
|
|
389
|
+
// Block variants system
|
|
390
|
+
variants: zod.z.record(zod.z.string(), NodeSchema).optional(),
|
|
391
|
+
defaultVariant: zod.z.string().optional(),
|
|
392
|
+
// Discovery metadata
|
|
393
|
+
category: blockCategoryEnum.optional(),
|
|
394
|
+
contentTypes: zod.z.array(zod.z.string()).optional(),
|
|
395
|
+
tags: zod.z.array(zod.z.string()).optional(),
|
|
396
|
+
icon: zod.z.string().optional()
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// ../blocks/src/utils/env.ts
|
|
400
|
+
function isDevEnvironment() {
|
|
401
|
+
try {
|
|
402
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV !== void 0) {
|
|
403
|
+
return process.env.NODE_ENV !== "production";
|
|
404
|
+
}
|
|
405
|
+
return true;
|
|
406
|
+
} catch {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ../blocks/src/system/node/types.ts
|
|
412
|
+
var RESERVED_KEYS = [
|
|
413
|
+
"children",
|
|
414
|
+
"key",
|
|
415
|
+
"$bind",
|
|
416
|
+
"$when",
|
|
417
|
+
"$repeat",
|
|
418
|
+
"$scopes"
|
|
419
|
+
];
|
|
420
|
+
function findReservedKeysInProps(props2) {
|
|
421
|
+
if (!props2) return [];
|
|
422
|
+
return RESERVED_KEYS.filter((key) => key in props2);
|
|
423
|
+
}
|
|
424
|
+
function validateProps(props2, context = "node builder") {
|
|
425
|
+
const reserved = findReservedKeysInProps(props2);
|
|
426
|
+
if (reserved.length === 0) return;
|
|
427
|
+
const message = `[blocks:${context}] Reserved keys found in props: ${reserved.join(", ")}. These should be passed as separate parameters or modifiers, not in props. For example, 'children' should be the 3rd parameter to el(), not in the props object.`;
|
|
428
|
+
if (isDevEnvironment()) {
|
|
429
|
+
throw new TypeError(message);
|
|
430
|
+
} else {
|
|
431
|
+
console.error(message);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// ../blocks/src/system/node/builder.ts
|
|
436
|
+
function normalizeChildren(children) {
|
|
437
|
+
if (!children) return void 0;
|
|
438
|
+
const flat = children.flat().filter(Boolean);
|
|
439
|
+
return flat.length > 0 ? flat : void 0;
|
|
440
|
+
}
|
|
441
|
+
function el(type, props2, children, ...mods) {
|
|
442
|
+
validateProps(props2, `el('${type}', ...)`);
|
|
443
|
+
const node = {
|
|
444
|
+
type,
|
|
445
|
+
...props2 && Object.keys(props2).length > 0 ? { props: props2 } : {},
|
|
446
|
+
...children && children.length ? { children: normalizeChildren(children) } : {}
|
|
447
|
+
};
|
|
448
|
+
if (mods && mods.length > 0) {
|
|
449
|
+
const validMods = mods.filter((mod) => typeof mod === "function");
|
|
450
|
+
if (validMods.length > 0) {
|
|
451
|
+
const modified = validMods.reduce((acc, fn) => fn(acc), node);
|
|
452
|
+
return devValidate(modified);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return devValidate(node);
|
|
456
|
+
}
|
|
457
|
+
var section = (props2, children, ...mods) => el("section", props2, children, ...mods);
|
|
458
|
+
var headerSection = (props2, children, ...mods) => el("headerSection", props2, children, ...mods);
|
|
459
|
+
var stack = (props2, children, ...mods) => el("stack", props2, children, ...mods);
|
|
460
|
+
var inline = (props2, children, ...mods) => el("inline", props2, children, ...mods);
|
|
461
|
+
var accordion = (props2, children, ...mods) => el("accordion", props2, children, ...mods);
|
|
462
|
+
var accordionItem = (props2, children, ...mods) => el("accordionItem", props2, children, ...mods);
|
|
463
|
+
var carousel = (props2, children, ...mods) => el("carousel", props2, children, ...mods);
|
|
464
|
+
var text = (props2, ...mods) => el("text", props2, void 0, ...mods);
|
|
465
|
+
var richText = (props2, ...mods) => el("richText", props2, void 0, ...mods);
|
|
466
|
+
var media = (props2, ...mods) => el("media", props2, void 0, ...mods);
|
|
467
|
+
var button = (props2, children, ...mods) => el("button", props2, children, ...mods);
|
|
468
|
+
var link = (props2, children, ...mods) => el("link", props2, children, ...mods);
|
|
469
|
+
var form = (props2, children, ...mods) => el("form", props2, children, ...mods);
|
|
470
|
+
var bookingForm = (props2, children, ...mods) => el("booking-form", props2, children, ...mods);
|
|
471
|
+
var eventRegistration = (props2, children, ...mods) => el("event-registration", props2, children, ...mods);
|
|
472
|
+
function bind(from, options) {
|
|
473
|
+
return (node) => ({
|
|
474
|
+
...node,
|
|
475
|
+
$bind: {
|
|
476
|
+
from,
|
|
477
|
+
...options?.fallback !== void 0 ? { fallback: options.fallback } : {},
|
|
478
|
+
...options?.transforms ? { transforms: options.transforms } : {},
|
|
479
|
+
...options?.pick ? { pick: options.pick } : {}
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
function when(path, options) {
|
|
484
|
+
return (node) => ({
|
|
485
|
+
...node,
|
|
486
|
+
$when: {
|
|
487
|
+
when: { from: path },
|
|
488
|
+
...options?.equals !== void 0 ? { equals: options.equals } : {},
|
|
489
|
+
...options?.not ? { not: true } : {}
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
function repeat(collectionPath, itemName = "item", options) {
|
|
494
|
+
return (node) => ({
|
|
495
|
+
...node,
|
|
496
|
+
$repeat: {
|
|
497
|
+
collection: { from: collectionPath, pick: "collection" },
|
|
498
|
+
itemName,
|
|
499
|
+
...options?.indexName ? { indexName: options.indexName } : {},
|
|
500
|
+
...options?.limit ? { limit: options.limit } : {},
|
|
501
|
+
...options?.sortBy ? { sortBy: { path: options.sortBy.path, direction: options.sortBy.direction ?? "asc" } } : {}
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
function props(extra) {
|
|
506
|
+
validateProps(extra, "props() modifier");
|
|
507
|
+
return (node) => ({
|
|
508
|
+
...node,
|
|
509
|
+
props: { ...node.props ?? {}, ...extra }
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
function accordionList({
|
|
513
|
+
collection,
|
|
514
|
+
itemName = "item",
|
|
515
|
+
indexName,
|
|
516
|
+
accordionProps = null,
|
|
517
|
+
itemProps = null,
|
|
518
|
+
triggerFrom,
|
|
519
|
+
contentFrom,
|
|
520
|
+
valueFrom
|
|
521
|
+
}, ...mods) {
|
|
522
|
+
const resolvedIndexName = indexName;
|
|
523
|
+
const resolvedValuePath = valueFrom === null ? null : valueFrom ?? resolvedIndexName;
|
|
524
|
+
const baseItemProps = {
|
|
525
|
+
...itemProps ?? {},
|
|
526
|
+
trigger: { $bind: { from: triggerFrom } }
|
|
527
|
+
};
|
|
528
|
+
{
|
|
529
|
+
baseItemProps.content = { $bind: { from: contentFrom } };
|
|
530
|
+
}
|
|
531
|
+
if (resolvedValuePath) {
|
|
532
|
+
baseItemProps.value = { $bind: { from: resolvedValuePath } };
|
|
533
|
+
}
|
|
534
|
+
return accordion(
|
|
535
|
+
accordionProps ?? void 0,
|
|
536
|
+
[
|
|
537
|
+
accordionItem(
|
|
538
|
+
baseItemProps,
|
|
539
|
+
void 0,
|
|
540
|
+
repeat(collection, itemName, { indexName: resolvedIndexName })
|
|
541
|
+
)
|
|
542
|
+
],
|
|
543
|
+
...mods
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
function devValidate(node) {
|
|
547
|
+
try {
|
|
548
|
+
if (isDevEnvironment()) {
|
|
549
|
+
const res = NodeSchema.safeParse(node);
|
|
550
|
+
if (!res.success) {
|
|
551
|
+
console.warn("[blocks:builder] Invalid node produced by builder", res.error.format());
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
} catch {
|
|
555
|
+
}
|
|
556
|
+
return node;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// ../blocks/src/system/node/fragments/backgroundLayer.ts
|
|
560
|
+
function backgroundLayer(path, options = {}) {
|
|
561
|
+
const {
|
|
562
|
+
styleClassName = "absolute inset-0 -z-10 h-full w-full pointer-events-none",
|
|
563
|
+
imageClassName
|
|
564
|
+
} = options;
|
|
565
|
+
const styleLayer = el("div", {
|
|
566
|
+
className: { $bind: { from: path, transforms: [{ id: "background.resolveClass", options: { baseClass: styleClassName } }] } },
|
|
567
|
+
style: { $bind: { from: path, transforms: [{ id: "background.resolveStyle" }] } }
|
|
568
|
+
});
|
|
569
|
+
const imageLayer = createBackgroundImageNode(path, imageClassName);
|
|
570
|
+
return [styleLayer, imageLayer];
|
|
571
|
+
}
|
|
572
|
+
function createBackgroundImageNode(path, baseClassName = "absolute -z-10") {
|
|
573
|
+
const imagePath = `${path}.image`;
|
|
574
|
+
return media(
|
|
575
|
+
{
|
|
576
|
+
className: {
|
|
577
|
+
$bind: {
|
|
578
|
+
from: path,
|
|
579
|
+
transforms: [{
|
|
580
|
+
id: "background.resolveImageClassName",
|
|
581
|
+
options: { baseClass: `background-image ${baseClassName}` }
|
|
582
|
+
}]
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
style: {
|
|
586
|
+
$bind: {
|
|
587
|
+
from: path,
|
|
588
|
+
transforms: [{ id: "background.resolveImageStyle" }]
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
when(imagePath),
|
|
593
|
+
bind(imagePath)
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// ../blocks/src/theme/utils/colorStyles.ts
|
|
598
|
+
var COLOR_VAR_PREFIX = "--tb-";
|
|
599
|
+
function parseToken(source) {
|
|
600
|
+
if (source.startsWith("raw:")) {
|
|
601
|
+
return { token: source, raw: source.slice(4) };
|
|
602
|
+
}
|
|
603
|
+
if (source.includes("/")) {
|
|
604
|
+
const [token, opacity] = source.split("/");
|
|
605
|
+
const alpha = Number(opacity) / 100;
|
|
606
|
+
if (!Number.isNaN(alpha)) {
|
|
607
|
+
return { token, alpha };
|
|
608
|
+
}
|
|
609
|
+
return { token: source };
|
|
610
|
+
}
|
|
611
|
+
return { token: source };
|
|
612
|
+
}
|
|
613
|
+
function rgbColorValue(token) {
|
|
614
|
+
const { token: baseToken, alpha, raw } = parseToken(token);
|
|
615
|
+
if (raw) {
|
|
616
|
+
return raw;
|
|
617
|
+
}
|
|
618
|
+
const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
|
|
619
|
+
if (alpha === void 0) {
|
|
620
|
+
return `rgb(var(${cssVar}))`;
|
|
621
|
+
}
|
|
622
|
+
return `rgba(var(${cssVar}), ${alpha})`;
|
|
623
|
+
}
|
|
624
|
+
function backgroundColorStyle(token) {
|
|
625
|
+
return { backgroundColor: rgbColorValue(token) };
|
|
626
|
+
}
|
|
627
|
+
function textColorStyle(token) {
|
|
628
|
+
return { color: rgbColorValue(token) };
|
|
629
|
+
}
|
|
630
|
+
function borderColorStyle(token) {
|
|
631
|
+
return { borderColor: rgbColorValue(token) };
|
|
632
|
+
}
|
|
633
|
+
function mergeStyles(...styles) {
|
|
634
|
+
const merged = styles.filter(Boolean).reduce((acc, style) => Object.assign(acc, style), {});
|
|
635
|
+
return Object.keys(merged).length ? merged : void 0;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// ../blocks/src/system/node/fragments/headingGroup.ts
|
|
639
|
+
function headingGroup(opts) {
|
|
640
|
+
const {
|
|
641
|
+
eyebrowPath,
|
|
642
|
+
titlePath,
|
|
643
|
+
containerClass = "text-center",
|
|
644
|
+
className,
|
|
645
|
+
eyebrowClass = "heading-eyebrow text-sm font-semibold tracking-wide",
|
|
646
|
+
titleClass = "heading-title text-3xl font-semibold sm:text-4xl",
|
|
647
|
+
eyebrowStyle = textColorStyle("neutral-500"),
|
|
648
|
+
titleStyle = textColorStyle("neutral-900")
|
|
649
|
+
} = opts;
|
|
650
|
+
const finalContainerClass = className ? `${containerClass} ${className}` : containerClass;
|
|
651
|
+
return stack({ gap: "sm", className: finalContainerClass }, [
|
|
652
|
+
eyebrowPath ? text({ as: "p", className: eyebrowClass, style: eyebrowStyle }, when(eyebrowPath), bind(eyebrowPath)) : null,
|
|
653
|
+
text({ as: "h2", className: titleClass, style: titleStyle }, bind(titlePath))
|
|
654
|
+
]);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// ../blocks/src/system/node/fragments/sectionContainer.ts
|
|
658
|
+
function sectionContainer(children, opts) {
|
|
659
|
+
const gap = opts?.gap ?? "md";
|
|
660
|
+
const align = opts?.align;
|
|
661
|
+
const bindFrom = opts?.bindFrom ?? "_containerStyles";
|
|
662
|
+
const additionalClasses = opts?.className ?? "";
|
|
663
|
+
return stack(
|
|
664
|
+
{
|
|
665
|
+
gap,
|
|
666
|
+
className: {
|
|
667
|
+
$bind: {
|
|
668
|
+
from: bindFrom,
|
|
669
|
+
transforms: [
|
|
670
|
+
{
|
|
671
|
+
id: "containerStyles.resolveClassName",
|
|
672
|
+
options: { baseClass: additionalClasses }
|
|
673
|
+
}
|
|
674
|
+
],
|
|
675
|
+
fallback: additionalClasses ? `container mx-auto ${additionalClasses}` : "container mx-auto"
|
|
676
|
+
}
|
|
677
|
+
},
|
|
678
|
+
...align ? { align } : {}
|
|
679
|
+
},
|
|
680
|
+
children
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// ../blocks/src/system/spacing.ts
|
|
685
|
+
var DEFAULT_SECTION_SPACING = "medium";
|
|
686
|
+
|
|
687
|
+
// ../blocks/src/system/node/fragments/styledSection.ts
|
|
688
|
+
function styledSection(config) {
|
|
689
|
+
const {
|
|
690
|
+
children,
|
|
691
|
+
baseClass = "px-6",
|
|
692
|
+
spacing = DEFAULT_SECTION_SPACING,
|
|
693
|
+
background = "background/base",
|
|
694
|
+
bindFrom = "_sectionStyles",
|
|
695
|
+
imageClassName = "absolute -z-10"
|
|
696
|
+
} = config;
|
|
697
|
+
const backgroundNodes = backgroundLayer(`${bindFrom}.background`, {
|
|
698
|
+
imageClassName
|
|
699
|
+
});
|
|
700
|
+
const childrenArray = Array.isArray(children) ? children : [children];
|
|
701
|
+
return section(
|
|
702
|
+
{
|
|
703
|
+
background,
|
|
704
|
+
className: {
|
|
705
|
+
$bind: {
|
|
706
|
+
from: bindFrom,
|
|
707
|
+
transforms: [
|
|
708
|
+
{
|
|
709
|
+
id: "sectionStyles.resolveClassName",
|
|
710
|
+
options: { baseClass, defaultSpacing: spacing }
|
|
711
|
+
}
|
|
712
|
+
]
|
|
713
|
+
// No fallback needed - transform handles all cases via defaultSpacing
|
|
714
|
+
}
|
|
715
|
+
},
|
|
716
|
+
allowOverflow: {
|
|
717
|
+
$bind: {
|
|
718
|
+
from: `${bindFrom}.background.overflow`,
|
|
719
|
+
fallback: false
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
},
|
|
723
|
+
[
|
|
724
|
+
...backgroundNodes,
|
|
725
|
+
...childrenArray
|
|
726
|
+
]
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// ../blocks/src/system/node/fragments/ctaButton.ts
|
|
731
|
+
function ctaButton(opts) {
|
|
732
|
+
const base = opts?.basePath ?? "cta";
|
|
733
|
+
const linkPath = opts?.linkPath ?? `${base}.link`;
|
|
734
|
+
const labelPath = opts?.labelPath ?? `${base}.label`;
|
|
735
|
+
const variantPath = opts?.variantPath ?? `${base}.variant`;
|
|
736
|
+
const iconLeftPath = opts?.iconLeftPath ?? `${base}.iconLeft`;
|
|
737
|
+
const iconRightPath = opts?.iconRightPath ?? `${base}.iconRight`;
|
|
738
|
+
const whenPath = opts?.whenPath ?? labelPath;
|
|
739
|
+
const leftIcon = media({ className: "mr-2 h-4 w-4 inline-block" }, when(iconLeftPath), bind(iconLeftPath));
|
|
740
|
+
const rightIcon = media({ className: "ml-2 h-4 w-4 inline-block" }, when(iconRightPath), bind(iconRightPath));
|
|
741
|
+
const label = text({ as: "span" }, bind(labelPath));
|
|
742
|
+
const classNameObj = {
|
|
743
|
+
$bind: {
|
|
744
|
+
from: variantPath,
|
|
745
|
+
fallback: "primary"
|
|
746
|
+
},
|
|
747
|
+
$prepend: "button-"
|
|
748
|
+
};
|
|
749
|
+
if (opts?.className) {
|
|
750
|
+
classNameObj.$append = ` ${opts.className}`;
|
|
751
|
+
}
|
|
752
|
+
const node = button(
|
|
753
|
+
{
|
|
754
|
+
className: classNameObj,
|
|
755
|
+
href: { $bind: { from: linkPath, transforms: [{ id: "links.resolve" }], fallback: "#" } }
|
|
756
|
+
},
|
|
757
|
+
[leftIcon, label, rightIcon]
|
|
758
|
+
);
|
|
759
|
+
if (opts?.repeatFrom) {
|
|
760
|
+
return button(
|
|
761
|
+
node.props ?? {},
|
|
762
|
+
node.children,
|
|
763
|
+
repeat(opts.repeatFrom.collectionPath, opts.repeatFrom.itemName ?? base)
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
return button(node.props ?? {}, node.children, when(whenPath));
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// ../blocks/src/system/node/fragments/ctaRow.ts
|
|
770
|
+
function ctaRow(opts) {
|
|
771
|
+
const collectionPath = opts?.collectionPath ?? "content.ctas";
|
|
772
|
+
const item = opts?.itemName ?? "cta";
|
|
773
|
+
const gap = opts?.gap ?? "sm";
|
|
774
|
+
const justify = opts?.justify ?? "center";
|
|
775
|
+
const containerClassName = ["cta-row flex-wrap", opts?.containerClassName].filter(Boolean).join(" ");
|
|
776
|
+
return inline(
|
|
777
|
+
{ gap, justify, className: containerClassName },
|
|
778
|
+
[
|
|
779
|
+
ctaButton({ basePath: item, repeatFrom: { collectionPath, itemName: item } })
|
|
780
|
+
],
|
|
781
|
+
when(collectionPath)
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// ../blocks/src/system/node/fragments/navRow.ts
|
|
786
|
+
function navRow(opts) {
|
|
787
|
+
const collectionPath = opts?.collectionPath ?? "menu.items";
|
|
788
|
+
const itemName = opts?.itemName ?? "navItem";
|
|
789
|
+
const gap = opts?.gap ?? "md";
|
|
790
|
+
const align = opts?.align ?? "end";
|
|
791
|
+
const className = ["items-center flex-wrap", opts?.className].filter(Boolean).join(" ").trim();
|
|
792
|
+
const linkClassName = opts?.linkClassName ?? "header-nav-link inline-flex items-center px-4 py-2 text-sm font-medium transition-theme-standard";
|
|
793
|
+
const links = link(
|
|
794
|
+
{
|
|
795
|
+
className: linkClassName,
|
|
796
|
+
href: { $bind: { from: `${itemName}.link`, transforms: [{ id: "links.resolve" }], fallback: "#" } },
|
|
797
|
+
target: { $bind: { from: `${itemName}.target` } },
|
|
798
|
+
rel: { $bind: { from: `${itemName}.rel` } },
|
|
799
|
+
"data-active": { $bind: { from: `${itemName}.active` } }
|
|
800
|
+
},
|
|
801
|
+
[text({ as: "span" }, bind(`${itemName}.label`))],
|
|
802
|
+
repeat(collectionPath, itemName)
|
|
803
|
+
);
|
|
804
|
+
return inline(
|
|
805
|
+
{ gap, align, className },
|
|
806
|
+
[links],
|
|
807
|
+
when(collectionPath)
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
var FRAGMENT_ID_PATTERN = /^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/i;
|
|
811
|
+
var FIELD_ID_PATTERN = /^[a-z][a-zA-Z0-9_-]*$/;
|
|
812
|
+
var dataLoaderSchema = zod.z.object({
|
|
813
|
+
endpoint: zod.z.string().min(1, "Fragment data loader requires an endpoint"),
|
|
814
|
+
params: zod.z.record(zod.z.string(), zod.z.unknown()).default({}),
|
|
815
|
+
mode: zod.z.enum(["server", "client"]).default("server")
|
|
816
|
+
});
|
|
817
|
+
var fragmentDataSchema = zod.z.object({
|
|
818
|
+
key: zod.z.string().min(1, "Fragment data key is required"),
|
|
819
|
+
loader: dataLoaderSchema.optional()
|
|
820
|
+
});
|
|
821
|
+
var fragmentConfigSchema = zod.z.object({
|
|
822
|
+
id: zod.z.string().min(1, "Fragment id is required").regex(FRAGMENT_ID_PATTERN, "Fragment id must be alphanumeric with optional . _ - separators"),
|
|
823
|
+
title: zod.z.string().optional(),
|
|
824
|
+
description: zod.z.string().optional(),
|
|
825
|
+
category: zod.z.enum(["content", "media", "interactive", "layout"]).optional(),
|
|
826
|
+
icon: zod.z.string().optional(),
|
|
827
|
+
fields: fieldSchema.array().default([]),
|
|
828
|
+
layout: zod.z.union([NodeSchema, NodeSchema.array()]).transform((value) => Array.isArray(value) ? value : [value]),
|
|
829
|
+
data: fragmentDataSchema.optional()
|
|
830
|
+
});
|
|
831
|
+
var FragmentConfigError = class extends Error {
|
|
832
|
+
constructor(message) {
|
|
833
|
+
super(message);
|
|
834
|
+
this.name = "FragmentConfigError";
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
function defineFragment(config) {
|
|
838
|
+
const parsed = fragmentConfigSchema.parse(config);
|
|
839
|
+
validateFieldDefinitions(parsed.fields, parsed.id);
|
|
840
|
+
return {
|
|
841
|
+
...parsed,
|
|
842
|
+
fields: parsed.fields.map(cloneFieldDefinition),
|
|
843
|
+
layout: parsed.layout.map((node) => ({ ...node }))
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
function scopeFragmentFields(fragment, scope) {
|
|
847
|
+
const normalizedScope = scope.trim();
|
|
848
|
+
if (!normalizedScope) {
|
|
849
|
+
return fragment.fields.map(cloneFieldDefinition);
|
|
850
|
+
}
|
|
851
|
+
return fragment.fields.map((field) => prefixFieldId(field, normalizedScope));
|
|
852
|
+
}
|
|
853
|
+
function scopeFragmentLayout(fragment, scope) {
|
|
854
|
+
const normalizedScope = scope.trim();
|
|
855
|
+
return fragment.layout.map((node) => cloneAndScopeNode(node, normalizedScope));
|
|
856
|
+
}
|
|
857
|
+
function validateFieldDefinitions(fields4, fragmentId) {
|
|
858
|
+
const seen = /* @__PURE__ */ new Set();
|
|
859
|
+
for (const field of fields4) {
|
|
860
|
+
if (field.id.includes(".")) {
|
|
861
|
+
throw new FragmentConfigError(
|
|
862
|
+
`Field "${field.id}" in fragment "${fragmentId}" should not include dot notation; the builder scopes fields automatically.`
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
if (!FIELD_ID_PATTERN.test(field.id)) {
|
|
866
|
+
throw new FragmentConfigError(
|
|
867
|
+
`Field "${field.id}" in fragment "${fragmentId}" must start with a letter and contain only alphanumeric, "_", or "-".`
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
if (seen.has(field.id)) {
|
|
871
|
+
throw new FragmentConfigError(
|
|
872
|
+
`Duplicate field id "${field.id}" found in fragment "${fragmentId}". Field ids must be unique per fragment.`
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
seen.add(field.id);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
function prefixFieldId(field, scope) {
|
|
879
|
+
const cloned = cloneFieldDefinition(field);
|
|
880
|
+
cloned.id = `${scope}.${field.id}`;
|
|
881
|
+
cloned.defaultValue = cloneUnknown(cloned.defaultValue);
|
|
882
|
+
cloned.visibleRoles = cloned.visibleRoles ? [...cloned.visibleRoles] : void 0;
|
|
883
|
+
cloned.ui = cloneUnknown(cloned.ui);
|
|
884
|
+
return cloned;
|
|
885
|
+
}
|
|
886
|
+
function cloneFieldDefinition(field) {
|
|
887
|
+
switch (field.type) {
|
|
888
|
+
case "repeater":
|
|
889
|
+
if (field.polymorphic && field.itemTypes) {
|
|
890
|
+
return {
|
|
891
|
+
...field,
|
|
892
|
+
polymorphic: true,
|
|
893
|
+
itemTypes: Object.fromEntries(
|
|
894
|
+
Object.entries(field.itemTypes).map(([key, itemType]) => [
|
|
895
|
+
key,
|
|
896
|
+
{
|
|
897
|
+
...itemType,
|
|
898
|
+
fields: itemType.fields.map(cloneFieldDefinition)
|
|
899
|
+
}
|
|
900
|
+
])
|
|
901
|
+
)
|
|
902
|
+
};
|
|
903
|
+
} else if (field.schema) {
|
|
904
|
+
return {
|
|
905
|
+
...field,
|
|
906
|
+
schema: {
|
|
907
|
+
fields: field.schema.fields.map(cloneFieldDefinition)
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
return { ...field };
|
|
912
|
+
case "group":
|
|
913
|
+
return {
|
|
914
|
+
...field,
|
|
915
|
+
schema: {
|
|
916
|
+
fields: field.schema.fields.map(cloneFieldDefinition)
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
default:
|
|
920
|
+
return { ...field };
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
function cloneUnknown(value) {
|
|
924
|
+
if (value == null || typeof value !== "object") {
|
|
925
|
+
return value;
|
|
926
|
+
}
|
|
927
|
+
if (Array.isArray(value)) {
|
|
928
|
+
return value.map((entry) => cloneUnknown(entry));
|
|
929
|
+
}
|
|
930
|
+
return { ...value };
|
|
931
|
+
}
|
|
932
|
+
function cloneAndScopeNode(node, scope) {
|
|
933
|
+
const cloned = {
|
|
934
|
+
...node
|
|
935
|
+
};
|
|
936
|
+
if (node.children) {
|
|
937
|
+
cloned.children = node.children.map((child) => cloneAndScopeNode(child, scope));
|
|
938
|
+
}
|
|
939
|
+
if (node.props) {
|
|
940
|
+
cloned.props = scopePropBindings(node.props, scope);
|
|
941
|
+
}
|
|
942
|
+
if (node.$bind) {
|
|
943
|
+
cloned.$bind = scopeBinding(node.$bind, scope);
|
|
944
|
+
}
|
|
945
|
+
if (node.$repeat) {
|
|
946
|
+
cloned.$repeat = scopeRepeat(node.$repeat, scope);
|
|
947
|
+
}
|
|
948
|
+
if (node.$when) {
|
|
949
|
+
cloned.$when = scopeCondition(node.$when, scope);
|
|
950
|
+
}
|
|
951
|
+
if (node.$scopes) {
|
|
952
|
+
cloned.$scopes = node.$scopes.map((entry) => ({
|
|
953
|
+
name: entry.name,
|
|
954
|
+
from: scopeBinding(entry.from, scope)
|
|
955
|
+
}));
|
|
956
|
+
}
|
|
957
|
+
return cloned;
|
|
958
|
+
}
|
|
959
|
+
function scopeBinding(binding, scope) {
|
|
960
|
+
const cloned = {
|
|
961
|
+
...binding,
|
|
962
|
+
transforms: binding.transforms?.map(
|
|
963
|
+
(step) => ({ ...step })
|
|
964
|
+
) ?? []
|
|
965
|
+
};
|
|
966
|
+
cloned.from = scopeContentPath(binding.from, scope);
|
|
967
|
+
return cloned;
|
|
968
|
+
}
|
|
969
|
+
function scopeRepeat(repeat2, scope) {
|
|
970
|
+
const cloned = {
|
|
971
|
+
...repeat2,
|
|
972
|
+
collection: scopeBinding(repeat2.collection, scope)
|
|
973
|
+
};
|
|
974
|
+
if (repeat2.sortBy) {
|
|
975
|
+
cloned.sortBy = {
|
|
976
|
+
...repeat2.sortBy,
|
|
977
|
+
path: scopeContentPath(repeat2.sortBy.path, scope)
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
return cloned;
|
|
981
|
+
}
|
|
982
|
+
function scopeCondition(condition, scope) {
|
|
983
|
+
return {
|
|
984
|
+
...condition,
|
|
985
|
+
when: scopeBinding(condition.when, scope)
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
function scopePropBindings(input, scope) {
|
|
989
|
+
const result = {};
|
|
990
|
+
for (const [key, value] of Object.entries(input)) {
|
|
991
|
+
result[key] = scopePropValue(value, scope);
|
|
992
|
+
}
|
|
993
|
+
return result;
|
|
994
|
+
}
|
|
995
|
+
function scopePropValue(value, scope) {
|
|
996
|
+
if (Array.isArray(value)) {
|
|
997
|
+
return value.map((entry) => scopePropValue(entry, scope));
|
|
998
|
+
}
|
|
999
|
+
if (value && typeof value === "object") {
|
|
1000
|
+
if ("$bind" in value) {
|
|
1001
|
+
const bindingConfig = value.$bind;
|
|
1002
|
+
return {
|
|
1003
|
+
$bind: scopeBinding(bindingConfig, scope)
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
const result = {};
|
|
1007
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1008
|
+
result[key] = scopePropValue(entry, scope);
|
|
1009
|
+
}
|
|
1010
|
+
return result;
|
|
1011
|
+
}
|
|
1012
|
+
return value;
|
|
1013
|
+
}
|
|
1014
|
+
function scopeContentPath(path, scope) {
|
|
1015
|
+
if (!scope || scope.length === 0) {
|
|
1016
|
+
return path;
|
|
1017
|
+
}
|
|
1018
|
+
if (path === "content") {
|
|
1019
|
+
return `content.${scope}`;
|
|
1020
|
+
}
|
|
1021
|
+
if (path.startsWith("content.")) {
|
|
1022
|
+
const remainder = path.slice("content.".length);
|
|
1023
|
+
return remainder.length > 0 ? `content.${scope}.${remainder}` : `content.${scope}`;
|
|
1024
|
+
}
|
|
1025
|
+
if (path.startsWith("content[")) {
|
|
1026
|
+
return path.replace(/^content/, `content.${scope}`);
|
|
1027
|
+
}
|
|
1028
|
+
if (path.startsWith("$root.")) {
|
|
1029
|
+
return path;
|
|
1030
|
+
}
|
|
1031
|
+
if (path.includes(".")) {
|
|
1032
|
+
return path;
|
|
1033
|
+
}
|
|
1034
|
+
return `content.${scope}.${path}`;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// ../blocks/src/system/fragments/builder.ts
|
|
1038
|
+
var FragmentCompositionError = class extends Error {
|
|
1039
|
+
constructor(message) {
|
|
1040
|
+
super(message);
|
|
1041
|
+
this.name = "FragmentCompositionError";
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
var DOT_SCOPE_REGEX = /^[A-Za-z0-9_.-]*$/;
|
|
1045
|
+
function materializeFragment(config) {
|
|
1046
|
+
const scope = normalizeScope(config.scope);
|
|
1047
|
+
const fields4 = scopeFragmentFields(config.fragment, scope);
|
|
1048
|
+
const layout = scopeFragmentLayout(config.fragment, scope);
|
|
1049
|
+
const fieldPriority = config.fieldPriority ?? 0;
|
|
1050
|
+
let data;
|
|
1051
|
+
if (config.fragment.data) {
|
|
1052
|
+
const key = config.dataKey ?? config.fragment.data.key;
|
|
1053
|
+
if (!key || typeof key !== "string") {
|
|
1054
|
+
throw new FragmentCompositionError(
|
|
1055
|
+
`Fragment "${config.fragment.id}" requires a data key to compose.`
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
data = {
|
|
1059
|
+
key,
|
|
1060
|
+
loader: config.fragment.data.loader ? { ...config.fragment.data.loader } : void 0
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
return {
|
|
1064
|
+
fragment: config.fragment,
|
|
1065
|
+
scope,
|
|
1066
|
+
fields: fields4,
|
|
1067
|
+
layout,
|
|
1068
|
+
data,
|
|
1069
|
+
fieldPriority
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
function composeFragments(instances) {
|
|
1073
|
+
const fieldEntries = [];
|
|
1074
|
+
const layouts = [];
|
|
1075
|
+
const dataEntries = [];
|
|
1076
|
+
const scopes = [];
|
|
1077
|
+
const seenFieldIds = /* @__PURE__ */ new Set();
|
|
1078
|
+
let fieldOrder = 0;
|
|
1079
|
+
for (const instance of instances) {
|
|
1080
|
+
const materialized = materializeFragment(instance);
|
|
1081
|
+
for (const field of materialized.fields) {
|
|
1082
|
+
if (seenFieldIds.has(field.id)) {
|
|
1083
|
+
throw new FragmentCompositionError(
|
|
1084
|
+
`Duplicate field id "${field.id}" when composing fragments.`
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
seenFieldIds.add(field.id);
|
|
1088
|
+
fieldEntries.push({
|
|
1089
|
+
id: field.id,
|
|
1090
|
+
field,
|
|
1091
|
+
priority: materialized.fieldPriority,
|
|
1092
|
+
order: fieldOrder++
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
scopes.push(materialized.scope);
|
|
1096
|
+
layouts.push(...materialized.layout);
|
|
1097
|
+
if (materialized.data) {
|
|
1098
|
+
if (dataEntries.find((entry) => entry.key === materialized.data?.key)) {
|
|
1099
|
+
throw new FragmentCompositionError(
|
|
1100
|
+
`Duplicate fragment data key "${materialized.data.key}" detected.`
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
dataEntries.push(materialized.data);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return {
|
|
1107
|
+
fields: fieldEntries.sort((a, b) => {
|
|
1108
|
+
if (a.priority !== b.priority) {
|
|
1109
|
+
return a.priority - b.priority;
|
|
1110
|
+
}
|
|
1111
|
+
return a.order - b.order;
|
|
1112
|
+
}).map((entry) => entry.field),
|
|
1113
|
+
layout: layouts,
|
|
1114
|
+
data: dataEntries,
|
|
1115
|
+
scopes
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
function normalizeScope(scope) {
|
|
1119
|
+
if (!scope) return "";
|
|
1120
|
+
const trimmed = scope.trim();
|
|
1121
|
+
if (!DOT_SCOPE_REGEX.test(trimmed)) {
|
|
1122
|
+
throw new FragmentCompositionError(
|
|
1123
|
+
`Fragment scope "${scope}" contains invalid characters. Use alphanumeric, ".", "-", or "_".`
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
return trimmed;
|
|
1127
|
+
}
|
|
1128
|
+
function buildFragmentDataLoaders(composition2) {
|
|
1129
|
+
const loaders = {};
|
|
1130
|
+
for (const entry of composition2.data) {
|
|
1131
|
+
if (!entry?.loader) continue;
|
|
1132
|
+
loaders[entry.key] = {
|
|
1133
|
+
endpoint: entry.loader.endpoint,
|
|
1134
|
+
params: { ...entry.loader.params ?? {} },
|
|
1135
|
+
mode: entry.loader.mode ?? "server"
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
return loaders;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// ../blocks/src/system/fragments/library/bodyCopy.ts
|
|
1142
|
+
var bodyCopyFragment = defineFragment({
|
|
1143
|
+
id: "bodyCopy",
|
|
1144
|
+
title: "Body Copy",
|
|
1145
|
+
description: "Heading, rich text body, and alignment controls.",
|
|
1146
|
+
fields: [
|
|
1147
|
+
{
|
|
1148
|
+
id: "heading",
|
|
1149
|
+
type: "text",
|
|
1150
|
+
label: "Heading",
|
|
1151
|
+
description: "Optional heading displayed above the body copy."
|
|
1152
|
+
},
|
|
1153
|
+
{
|
|
1154
|
+
id: "body",
|
|
1155
|
+
type: "richText",
|
|
1156
|
+
label: "Body",
|
|
1157
|
+
required: true
|
|
1158
|
+
},
|
|
1159
|
+
{
|
|
1160
|
+
id: "alignment",
|
|
1161
|
+
type: "select",
|
|
1162
|
+
label: "Text alignment",
|
|
1163
|
+
required: false,
|
|
1164
|
+
defaultValue: "left",
|
|
1165
|
+
options: [
|
|
1166
|
+
{ value: "left", label: "Left" },
|
|
1167
|
+
{ value: "center", label: "Center" }
|
|
1168
|
+
]
|
|
1169
|
+
}
|
|
1170
|
+
],
|
|
1171
|
+
layout: stack(
|
|
1172
|
+
{
|
|
1173
|
+
gap: "md",
|
|
1174
|
+
className: "mx-auto max-w-3xl",
|
|
1175
|
+
align: {
|
|
1176
|
+
$bind: {
|
|
1177
|
+
from: "content.alignment",
|
|
1178
|
+
transforms: [{ id: "ui.stackAlignFromAlignment" }],
|
|
1179
|
+
fallback: "start"
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
},
|
|
1183
|
+
[
|
|
1184
|
+
text(
|
|
1185
|
+
{
|
|
1186
|
+
as: "h2",
|
|
1187
|
+
className: {
|
|
1188
|
+
$bind: {
|
|
1189
|
+
from: "content.alignment",
|
|
1190
|
+
transforms: [
|
|
1191
|
+
{
|
|
1192
|
+
id: "ui.headingClassFromAlignment",
|
|
1193
|
+
options: {
|
|
1194
|
+
base: "text-3xl font-semibold sm:text-4xl"
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
],
|
|
1198
|
+
fallback: "text-3xl font-semibold sm:text-4xl"
|
|
1199
|
+
}
|
|
1200
|
+
},
|
|
1201
|
+
style: textColorStyle("neutral-900")
|
|
1202
|
+
},
|
|
1203
|
+
when("content.heading"),
|
|
1204
|
+
bind("content.heading")
|
|
1205
|
+
),
|
|
1206
|
+
richText(
|
|
1207
|
+
{
|
|
1208
|
+
className: {
|
|
1209
|
+
$bind: {
|
|
1210
|
+
from: "content.alignment",
|
|
1211
|
+
transforms: [
|
|
1212
|
+
{
|
|
1213
|
+
id: "ui.bodyClassFromAlignment",
|
|
1214
|
+
options: {
|
|
1215
|
+
base: "prose prose-lg max-w-none sm:prose-xl"
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
],
|
|
1219
|
+
fallback: "prose prose-lg max-w-none sm:prose-xl"
|
|
1220
|
+
},
|
|
1221
|
+
style: textColorStyle("neutral-600")
|
|
1222
|
+
}
|
|
1223
|
+
},
|
|
1224
|
+
bind("content.body")
|
|
1225
|
+
)
|
|
1226
|
+
]
|
|
1227
|
+
)
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
// ../blocks/src/system/fragments/library/heroCopy.ts
|
|
1231
|
+
var heroCopyFragment = defineFragment({
|
|
1232
|
+
id: "heroCopy",
|
|
1233
|
+
title: "Hero Copy",
|
|
1234
|
+
description: "Eyebrow, headline, and subheadline copy for hero layouts.",
|
|
1235
|
+
fields: [
|
|
1236
|
+
{
|
|
1237
|
+
id: "eyebrow",
|
|
1238
|
+
type: "text",
|
|
1239
|
+
label: "Eyebrow",
|
|
1240
|
+
description: "Optional short line above the headline",
|
|
1241
|
+
visibleRoles: ["designer", "admin"]
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
id: "headline",
|
|
1245
|
+
type: "text",
|
|
1246
|
+
label: "Headline",
|
|
1247
|
+
required: true,
|
|
1248
|
+
maxLength: 120
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
id: "subheadline",
|
|
1252
|
+
type: "text",
|
|
1253
|
+
label: "Subheadline",
|
|
1254
|
+
multiline: true,
|
|
1255
|
+
maxLength: 280
|
|
1256
|
+
}
|
|
1257
|
+
],
|
|
1258
|
+
layout: [
|
|
1259
|
+
text(
|
|
1260
|
+
{
|
|
1261
|
+
as: "p",
|
|
1262
|
+
className: "hero-eyebrow text-sm font-semibold tracking-wide",
|
|
1263
|
+
style: textColorStyle("neutral-500")
|
|
1264
|
+
},
|
|
1265
|
+
when("content.eyebrow"),
|
|
1266
|
+
bind("content.eyebrow")
|
|
1267
|
+
),
|
|
1268
|
+
text(
|
|
1269
|
+
{
|
|
1270
|
+
as: "h1",
|
|
1271
|
+
className: "hero-headline text-4xl font-semibold sm:text-5xl md:text-6xl",
|
|
1272
|
+
style: textColorStyle("neutral-900")
|
|
1273
|
+
},
|
|
1274
|
+
bind("content.headline")
|
|
1275
|
+
),
|
|
1276
|
+
text(
|
|
1277
|
+
{
|
|
1278
|
+
as: "p",
|
|
1279
|
+
className: "hero-subheadline text-lg sm:text-xl",
|
|
1280
|
+
style: textColorStyle("neutral-600")
|
|
1281
|
+
},
|
|
1282
|
+
when("content.subheadline"),
|
|
1283
|
+
bind("content.subheadline")
|
|
1284
|
+
)
|
|
1285
|
+
]
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
// ../blocks/src/system/fields/button.ts
|
|
1289
|
+
function createButtonGroup(options = {}) {
|
|
1290
|
+
const {
|
|
1291
|
+
variants = [
|
|
1292
|
+
{ value: "primary", label: "Primary" },
|
|
1293
|
+
{ value: "secondary", label: "Secondary" },
|
|
1294
|
+
{ value: "outline", label: "Outline" }
|
|
1295
|
+
],
|
|
1296
|
+
showGroupLabel = false,
|
|
1297
|
+
groupId = "button",
|
|
1298
|
+
groupLabel = "Button",
|
|
1299
|
+
flattenInRepeater = true
|
|
1300
|
+
} = options;
|
|
1301
|
+
const mainFields = [
|
|
1302
|
+
{
|
|
1303
|
+
id: "label",
|
|
1304
|
+
type: "text",
|
|
1305
|
+
label: "Label",
|
|
1306
|
+
required: true,
|
|
1307
|
+
multiline: false,
|
|
1308
|
+
maxLength: 40,
|
|
1309
|
+
ui: { row: "primary" }
|
|
1310
|
+
},
|
|
1311
|
+
{
|
|
1312
|
+
id: "variant",
|
|
1313
|
+
type: "select",
|
|
1314
|
+
label: "Style",
|
|
1315
|
+
options: variants,
|
|
1316
|
+
required: false,
|
|
1317
|
+
multiple: false,
|
|
1318
|
+
ui: { row: "primary" }
|
|
1319
|
+
},
|
|
1320
|
+
{
|
|
1321
|
+
id: "link",
|
|
1322
|
+
type: "link",
|
|
1323
|
+
label: "Link",
|
|
1324
|
+
required: true,
|
|
1325
|
+
ui: { colSpan: 2 }
|
|
1326
|
+
}
|
|
1327
|
+
];
|
|
1328
|
+
const iconsGroup = {
|
|
1329
|
+
id: "icons",
|
|
1330
|
+
type: "group",
|
|
1331
|
+
label: "Icons",
|
|
1332
|
+
required: false,
|
|
1333
|
+
ui: { preset: "disclosure", colSpan: 2 },
|
|
1334
|
+
schema: {
|
|
1335
|
+
fields: [
|
|
1336
|
+
{ id: "iconLeft", type: "media", label: "Left icon", required: false, mediaKinds: ["image"] },
|
|
1337
|
+
{ id: "iconRight", type: "media", label: "Right icon", required: false, mediaKinds: ["image"] }
|
|
1338
|
+
]
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
return {
|
|
1342
|
+
id: groupId,
|
|
1343
|
+
type: "group",
|
|
1344
|
+
label: groupLabel,
|
|
1345
|
+
ui: { layout: "grid", columns: 2, flattenInRepeater, hideLabel: !showGroupLabel },
|
|
1346
|
+
schema: { fields: [...mainFields, iconsGroup] },
|
|
1347
|
+
required: false
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
// ../blocks/src/system/fields/ctas.ts
|
|
1352
|
+
function createCtasRepeater(options = {}) {
|
|
1353
|
+
const {
|
|
1354
|
+
id = "ctas",
|
|
1355
|
+
label = "Calls to action",
|
|
1356
|
+
itemLabel = "CTA",
|
|
1357
|
+
minItems = 0,
|
|
1358
|
+
maxItems
|
|
1359
|
+
} = options;
|
|
1360
|
+
return {
|
|
1361
|
+
id,
|
|
1362
|
+
type: "repeater",
|
|
1363
|
+
label,
|
|
1364
|
+
itemLabel,
|
|
1365
|
+
itemLabelSource: "label",
|
|
1366
|
+
minItems,
|
|
1367
|
+
maxItems,
|
|
1368
|
+
schema: {
|
|
1369
|
+
fields: [createButtonGroup({ showGroupLabel: false, flattenInRepeater: true })]
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
// ../blocks/src/system/fragments/library/ctaRow.ts
|
|
1375
|
+
var ctaRowFragment = defineFragment({
|
|
1376
|
+
id: "ctaRow",
|
|
1377
|
+
title: "CTA Row",
|
|
1378
|
+
description: "Repeatable calls to action rendered inline.",
|
|
1379
|
+
fields: [createCtasRepeater({ label: "Calls to action", itemLabel: "CTA", maxItems: 3 })],
|
|
1380
|
+
layout: [
|
|
1381
|
+
ctaRow({ collectionPath: "content.ctas", itemName: "cta", justify: "center" })
|
|
1382
|
+
]
|
|
1383
|
+
});
|
|
1384
|
+
|
|
1385
|
+
// ../blocks/src/system/fragments/library/ctaCopy.ts
|
|
1386
|
+
var ctaCopyFragment = defineFragment({
|
|
1387
|
+
id: "ctaCopy",
|
|
1388
|
+
title: "CTA Copy",
|
|
1389
|
+
description: "Eyebrow, title, and supporting rich text for CTA layouts.",
|
|
1390
|
+
fields: [
|
|
1391
|
+
{
|
|
1392
|
+
id: "eyebrow",
|
|
1393
|
+
type: "text",
|
|
1394
|
+
label: "Eyebrow",
|
|
1395
|
+
description: "Optional short line above the title",
|
|
1396
|
+
visibleRoles: ["designer", "admin"]
|
|
1397
|
+
},
|
|
1398
|
+
{
|
|
1399
|
+
id: "title",
|
|
1400
|
+
type: "text",
|
|
1401
|
+
label: "Title",
|
|
1402
|
+
required: true,
|
|
1403
|
+
maxLength: 120
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
id: "content",
|
|
1407
|
+
type: "richText",
|
|
1408
|
+
label: "Copy",
|
|
1409
|
+
description: "Short paragraph of supporting copy."
|
|
1410
|
+
}
|
|
1411
|
+
],
|
|
1412
|
+
layout: [
|
|
1413
|
+
headingGroup({ eyebrowPath: "content.eyebrow", titlePath: "content.title", className: "cta-heading" }),
|
|
1414
|
+
richText(
|
|
1415
|
+
{
|
|
1416
|
+
className: "cta-content prose prose-lg mx-auto max-w-none sm:prose-xl",
|
|
1417
|
+
style: textColorStyle("neutral-700")
|
|
1418
|
+
},
|
|
1419
|
+
when("content.content"),
|
|
1420
|
+
bind("content.content")
|
|
1421
|
+
)
|
|
1422
|
+
]
|
|
1423
|
+
});
|
|
1424
|
+
|
|
1425
|
+
// ../blocks/src/system/fragments/library/testimonialsHeading.ts
|
|
1426
|
+
var testimonialsHeadingFragment = defineFragment({
|
|
1427
|
+
id: "testimonialsHeading",
|
|
1428
|
+
title: "Testimonials Heading",
|
|
1429
|
+
description: "Heading and subheading copy for testimonials section.",
|
|
1430
|
+
fields: [
|
|
1431
|
+
{
|
|
1432
|
+
id: "heading",
|
|
1433
|
+
type: "text",
|
|
1434
|
+
label: "Heading",
|
|
1435
|
+
description: "Optional block heading displayed above the testimonials."
|
|
1436
|
+
},
|
|
1437
|
+
{
|
|
1438
|
+
id: "subheading",
|
|
1439
|
+
type: "text",
|
|
1440
|
+
label: "Description",
|
|
1441
|
+
multiline: true,
|
|
1442
|
+
description: "Optional supporting copy shown below the heading."
|
|
1443
|
+
}
|
|
1444
|
+
],
|
|
1445
|
+
layout: [
|
|
1446
|
+
stack(
|
|
1447
|
+
{ gap: "md", className: "mx-auto max-w-2xl text-center" },
|
|
1448
|
+
[
|
|
1449
|
+
text(
|
|
1450
|
+
{ as: "h2", className: "text-3xl font-semibold sm:text-4xl", style: textColorStyle("neutral-900") },
|
|
1451
|
+
when("content.heading"),
|
|
1452
|
+
bind("content.heading")
|
|
1453
|
+
),
|
|
1454
|
+
text(
|
|
1455
|
+
{ as: "p", className: "text-lg sm:text-xl", style: textColorStyle("neutral-600") },
|
|
1456
|
+
when("content.subheading"),
|
|
1457
|
+
bind("content.subheading")
|
|
1458
|
+
)
|
|
1459
|
+
]
|
|
1460
|
+
)
|
|
1461
|
+
]
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
// ../blocks/src/system/fragments/library/testimonialsCarousel.ts
|
|
1465
|
+
var testimonialsCarouselFragment = defineFragment({
|
|
1466
|
+
id: "testimonialsCarousel",
|
|
1467
|
+
title: "Testimonials Carousel",
|
|
1468
|
+
description: "Carousel layout for testimonial cards with data loader configuration.",
|
|
1469
|
+
fields: [
|
|
1470
|
+
{
|
|
1471
|
+
id: "slidesToShow",
|
|
1472
|
+
type: "select",
|
|
1473
|
+
label: "Testimonials per view",
|
|
1474
|
+
defaultValue: "1",
|
|
1475
|
+
options: [
|
|
1476
|
+
{ value: "1", label: "One at a time" },
|
|
1477
|
+
{ value: "2", label: "Two at a time" },
|
|
1478
|
+
{ value: "3", label: "Three at a time" }
|
|
1479
|
+
]
|
|
1480
|
+
},
|
|
1481
|
+
{
|
|
1482
|
+
id: "transition",
|
|
1483
|
+
type: "select",
|
|
1484
|
+
label: "Transition",
|
|
1485
|
+
defaultValue: "slide",
|
|
1486
|
+
options: [
|
|
1487
|
+
{ value: "slide", label: "Slide" },
|
|
1488
|
+
{ value: "fade", label: "Fade" }
|
|
1489
|
+
]
|
|
1490
|
+
},
|
|
1491
|
+
{
|
|
1492
|
+
id: "maxEntries",
|
|
1493
|
+
type: "select",
|
|
1494
|
+
label: "Testimonials to load",
|
|
1495
|
+
defaultValue: "6",
|
|
1496
|
+
options: [
|
|
1497
|
+
{ value: "3", label: "3 testimonials" },
|
|
1498
|
+
{ value: "6", label: "6 testimonials" },
|
|
1499
|
+
{ value: "9", label: "9 testimonials" }
|
|
1500
|
+
]
|
|
1501
|
+
}
|
|
1502
|
+
],
|
|
1503
|
+
layout: [
|
|
1504
|
+
carousel(
|
|
1505
|
+
{
|
|
1506
|
+
className: "mt-12",
|
|
1507
|
+
slidesToShow: { $bind: { from: "slidesToShow", fallback: 1 } },
|
|
1508
|
+
transition: { $bind: { from: "transition", fallback: "slide" } },
|
|
1509
|
+
showControls: true
|
|
1510
|
+
},
|
|
1511
|
+
[
|
|
1512
|
+
stack(
|
|
1513
|
+
{
|
|
1514
|
+
gap: "lg",
|
|
1515
|
+
className: "h-full justify-between rounded-2xl border p-8 shadow-sm",
|
|
1516
|
+
style: mergeStyles(
|
|
1517
|
+
borderColorStyle("neutral-200"),
|
|
1518
|
+
backgroundColorStyle("surface")
|
|
1519
|
+
)
|
|
1520
|
+
},
|
|
1521
|
+
[
|
|
1522
|
+
richText(
|
|
1523
|
+
{
|
|
1524
|
+
className: "prose prose-lg max-w-none sm:prose-xl",
|
|
1525
|
+
style: textColorStyle("neutral-700")
|
|
1526
|
+
},
|
|
1527
|
+
bind("testimonial.content.body"),
|
|
1528
|
+
when("testimonial.content.body")
|
|
1529
|
+
),
|
|
1530
|
+
inline(
|
|
1531
|
+
{ gap: "md", className: "mt-6 items-center" },
|
|
1532
|
+
[
|
|
1533
|
+
media(
|
|
1534
|
+
{ className: "h-12 w-12 shrink-0 rounded-full object-cover" },
|
|
1535
|
+
bind("testimonial.content.headshot", { transforms: [{ id: "media.fromUrl" }] }),
|
|
1536
|
+
when("testimonial.content.headshot")
|
|
1537
|
+
),
|
|
1538
|
+
stack(
|
|
1539
|
+
{ gap: "xs" },
|
|
1540
|
+
[
|
|
1541
|
+
text(
|
|
1542
|
+
{ as: "p", className: "font-semibold", style: textColorStyle("neutral-900") },
|
|
1543
|
+
bind("testimonial.content.name", { fallback: "Anonymous" })
|
|
1544
|
+
),
|
|
1545
|
+
text(
|
|
1546
|
+
{ as: "p", className: "text-sm", style: textColorStyle("neutral-500") },
|
|
1547
|
+
when("testimonial.content.jobTitle"),
|
|
1548
|
+
bind("testimonial.content.jobTitle")
|
|
1549
|
+
),
|
|
1550
|
+
text(
|
|
1551
|
+
{ as: "p", className: "text-sm", style: textColorStyle("neutral-500") },
|
|
1552
|
+
when("testimonial.content.company"),
|
|
1553
|
+
bind("testimonial.content.company")
|
|
1554
|
+
)
|
|
1555
|
+
]
|
|
1556
|
+
)
|
|
1557
|
+
]
|
|
1558
|
+
)
|
|
1559
|
+
],
|
|
1560
|
+
repeat("data.entries", "testimonial")
|
|
1561
|
+
)
|
|
1562
|
+
]
|
|
1563
|
+
)
|
|
1564
|
+
],
|
|
1565
|
+
data: {
|
|
1566
|
+
key: "entries",
|
|
1567
|
+
loader: {
|
|
1568
|
+
endpoint: "listPublishedEntries",
|
|
1569
|
+
params: {
|
|
1570
|
+
type: "testimonial",
|
|
1571
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
1572
|
+
limit: { $bind: { from: "maxEntries", fallback: "6" } },
|
|
1573
|
+
stage: { $bind: { from: "$root.previewStage", fallback: "published" } }
|
|
1574
|
+
},
|
|
1575
|
+
mode: "server"
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1579
|
+
|
|
1580
|
+
// ../blocks/src/system/fragments/library/formCopy.ts
|
|
1581
|
+
var formCopyFragment = defineFragment({
|
|
1582
|
+
id: "formCopy",
|
|
1583
|
+
title: "Form Copy",
|
|
1584
|
+
description: "Optional title and introduction content for form blocks.",
|
|
1585
|
+
fields: [
|
|
1586
|
+
{
|
|
1587
|
+
id: "title",
|
|
1588
|
+
type: "text",
|
|
1589
|
+
label: "Title"
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
id: "intro",
|
|
1593
|
+
type: "richText",
|
|
1594
|
+
label: "Intro",
|
|
1595
|
+
description: "Optional introduction above the form."
|
|
1596
|
+
}
|
|
1597
|
+
],
|
|
1598
|
+
layout: [
|
|
1599
|
+
text(
|
|
1600
|
+
{
|
|
1601
|
+
as: "h2",
|
|
1602
|
+
className: "text-2xl font-semibold",
|
|
1603
|
+
style: textColorStyle("text")
|
|
1604
|
+
},
|
|
1605
|
+
when("content.title"),
|
|
1606
|
+
bind("content.title")
|
|
1607
|
+
),
|
|
1608
|
+
richText(
|
|
1609
|
+
{
|
|
1610
|
+
className: "text-base",
|
|
1611
|
+
style: textColorStyle("text")
|
|
1612
|
+
},
|
|
1613
|
+
when("content.intro"),
|
|
1614
|
+
bind("content.intro")
|
|
1615
|
+
)
|
|
1616
|
+
]
|
|
1617
|
+
});
|
|
1618
|
+
|
|
1619
|
+
// ../blocks/src/system/fragments/library/formEmbed.ts
|
|
1620
|
+
var formEmbedFragment = defineFragment({
|
|
1621
|
+
id: "formEmbed",
|
|
1622
|
+
title: "Form Embed",
|
|
1623
|
+
description: "Embeds a saved form with configurable submit button copy.",
|
|
1624
|
+
fields: [
|
|
1625
|
+
{
|
|
1626
|
+
id: "formId",
|
|
1627
|
+
type: "reference",
|
|
1628
|
+
label: "Form",
|
|
1629
|
+
description: "Pick a saved form to render.",
|
|
1630
|
+
required: true,
|
|
1631
|
+
referenceKind: "form",
|
|
1632
|
+
allowManualEntry: false
|
|
1633
|
+
},
|
|
1634
|
+
{
|
|
1635
|
+
id: "submitLabel",
|
|
1636
|
+
type: "text",
|
|
1637
|
+
label: "Submit button label",
|
|
1638
|
+
defaultValue: "Submit"
|
|
1639
|
+
},
|
|
1640
|
+
{
|
|
1641
|
+
id: "successMessage",
|
|
1642
|
+
type: "text",
|
|
1643
|
+
label: "Success message",
|
|
1644
|
+
description: "Shown after successful submit (handled by page or block)."
|
|
1645
|
+
}
|
|
1646
|
+
],
|
|
1647
|
+
layout: [
|
|
1648
|
+
form(
|
|
1649
|
+
{
|
|
1650
|
+
submitLabel: { $bind: { from: "submitLabel" } },
|
|
1651
|
+
successMessage: { $bind: { from: "successMessage" } }
|
|
1652
|
+
},
|
|
1653
|
+
[],
|
|
1654
|
+
bind("data.form")
|
|
1655
|
+
)
|
|
1656
|
+
],
|
|
1657
|
+
data: {
|
|
1658
|
+
key: "form",
|
|
1659
|
+
loader: {
|
|
1660
|
+
endpoint: "getPublicFormById",
|
|
1661
|
+
params: {
|
|
1662
|
+
formId: { $bind: { from: "formId" } }
|
|
1663
|
+
},
|
|
1664
|
+
mode: "server"
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
});
|
|
1668
|
+
|
|
1669
|
+
// ../blocks/src/system/fragments/library/footerBottomText.ts
|
|
1670
|
+
var footerBottomTextFragment = defineFragment({
|
|
1671
|
+
id: "footerBottomText",
|
|
1672
|
+
title: "Footer Bottom Text",
|
|
1673
|
+
description: "Rich text content displayed at the bottom of the footer.",
|
|
1674
|
+
fields: [
|
|
1675
|
+
{
|
|
1676
|
+
id: "bottomText",
|
|
1677
|
+
type: "richText",
|
|
1678
|
+
label: "Bottom text",
|
|
1679
|
+
description: "Appears at the very bottom of the footer.",
|
|
1680
|
+
ui: { variant: "limited" }
|
|
1681
|
+
}
|
|
1682
|
+
],
|
|
1683
|
+
layout: [
|
|
1684
|
+
richText(
|
|
1685
|
+
{
|
|
1686
|
+
className: "prose prose-sm mx-auto max-w-3xl text-center",
|
|
1687
|
+
style: textColorStyle("text/80")
|
|
1688
|
+
},
|
|
1689
|
+
when("bottomText"),
|
|
1690
|
+
bind("bottomText")
|
|
1691
|
+
)
|
|
1692
|
+
]
|
|
1693
|
+
});
|
|
1694
|
+
|
|
1695
|
+
// ../blocks/src/system/fragments/library/footerLinkGroups.ts
|
|
1696
|
+
var footerLinkGroupsFragment = defineFragment({
|
|
1697
|
+
id: "footerLinkGroups",
|
|
1698
|
+
title: "Footer Link Groups",
|
|
1699
|
+
description: "Repeating columns of grouped footer links.",
|
|
1700
|
+
fields: [
|
|
1701
|
+
{
|
|
1702
|
+
id: "linkGroups",
|
|
1703
|
+
type: "repeater",
|
|
1704
|
+
label: "Link groups",
|
|
1705
|
+
description: "Organize footer links into columns.",
|
|
1706
|
+
schema: {
|
|
1707
|
+
fields: [
|
|
1708
|
+
{
|
|
1709
|
+
id: "title",
|
|
1710
|
+
type: "text",
|
|
1711
|
+
label: "Group title"
|
|
1712
|
+
},
|
|
1713
|
+
{
|
|
1714
|
+
id: "links",
|
|
1715
|
+
type: "repeater",
|
|
1716
|
+
label: "Links",
|
|
1717
|
+
schema: {
|
|
1718
|
+
fields: [
|
|
1719
|
+
{
|
|
1720
|
+
id: "label",
|
|
1721
|
+
type: "text",
|
|
1722
|
+
label: "Label",
|
|
1723
|
+
required: true
|
|
1724
|
+
},
|
|
1725
|
+
{
|
|
1726
|
+
id: "link",
|
|
1727
|
+
type: "link",
|
|
1728
|
+
label: "Link",
|
|
1729
|
+
required: true
|
|
1730
|
+
}
|
|
1731
|
+
]
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
]
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
],
|
|
1738
|
+
layout: [
|
|
1739
|
+
stack(
|
|
1740
|
+
{
|
|
1741
|
+
gap: "lg",
|
|
1742
|
+
align: "start",
|
|
1743
|
+
className: "grid gap-8 md:grid-cols-3"
|
|
1744
|
+
},
|
|
1745
|
+
[
|
|
1746
|
+
stack(
|
|
1747
|
+
{ gap: "md", align: "start", className: "text-left" },
|
|
1748
|
+
[
|
|
1749
|
+
text(
|
|
1750
|
+
{
|
|
1751
|
+
as: "h3",
|
|
1752
|
+
className: "text-xs font-semibold uppercase tracking-wide",
|
|
1753
|
+
style: textColorStyle("text/70")
|
|
1754
|
+
},
|
|
1755
|
+
when("group.title"),
|
|
1756
|
+
bind("group.title")
|
|
1757
|
+
),
|
|
1758
|
+
stack(
|
|
1759
|
+
{ gap: "sm", align: "start", className: "space-y-2" },
|
|
1760
|
+
[
|
|
1761
|
+
link(
|
|
1762
|
+
{
|
|
1763
|
+
className: "block text-sm transition-theme hover:opacity-80",
|
|
1764
|
+
style: textColorStyle("text"),
|
|
1765
|
+
href: {
|
|
1766
|
+
$bind: {
|
|
1767
|
+
from: "entry.link",
|
|
1768
|
+
transforms: [{ id: "links.resolve" }],
|
|
1769
|
+
fallback: "#"
|
|
1770
|
+
}
|
|
1771
|
+
},
|
|
1772
|
+
target: { $bind: { from: "entry.target" } },
|
|
1773
|
+
rel: { $bind: { from: "entry.rel" } }
|
|
1774
|
+
},
|
|
1775
|
+
[text({ as: "span" }, bind("entry.label"))],
|
|
1776
|
+
repeat("group.links", "entry")
|
|
1777
|
+
)
|
|
1778
|
+
]
|
|
1779
|
+
)
|
|
1780
|
+
],
|
|
1781
|
+
repeat("linkGroups", "group")
|
|
1782
|
+
// Relative path
|
|
1783
|
+
)
|
|
1784
|
+
],
|
|
1785
|
+
when("linkGroups")
|
|
1786
|
+
// Relative path
|
|
1787
|
+
)
|
|
1788
|
+
]
|
|
1789
|
+
});
|
|
1790
|
+
|
|
1791
|
+
// ../blocks/src/system/fragments/library/blogFeaturedPost.ts
|
|
1792
|
+
var blogFeaturedPostFragment = defineFragment({
|
|
1793
|
+
id: "blogFeaturedPost",
|
|
1794
|
+
title: "Featured Blog Post",
|
|
1795
|
+
description: "Featured blog post card with title and preview content.",
|
|
1796
|
+
fields: [
|
|
1797
|
+
{
|
|
1798
|
+
id: "title",
|
|
1799
|
+
type: "text",
|
|
1800
|
+
label: "Block title",
|
|
1801
|
+
description: "Displayed above the featured blog post.",
|
|
1802
|
+
defaultValue: "Latest post",
|
|
1803
|
+
maxLength: 120
|
|
1804
|
+
},
|
|
1805
|
+
{
|
|
1806
|
+
id: "postSlug",
|
|
1807
|
+
type: "reference",
|
|
1808
|
+
label: "Blog post",
|
|
1809
|
+
description: "Pick the blog post to feature in this block.",
|
|
1810
|
+
required: true,
|
|
1811
|
+
referenceKind: "post",
|
|
1812
|
+
allowManualEntry: false
|
|
1813
|
+
}
|
|
1814
|
+
],
|
|
1815
|
+
layout: [
|
|
1816
|
+
sectionContainer(
|
|
1817
|
+
[
|
|
1818
|
+
stack({ gap: "sm", className: "md:w-1/3" }, [
|
|
1819
|
+
text(
|
|
1820
|
+
{
|
|
1821
|
+
as: "h2",
|
|
1822
|
+
className: "text-2xl font-semibold md:text-3xl",
|
|
1823
|
+
style: textColorStyle("neutral-900")
|
|
1824
|
+
},
|
|
1825
|
+
bind("title", { fallback: "Latest post" })
|
|
1826
|
+
// Relative path
|
|
1827
|
+
)
|
|
1828
|
+
]),
|
|
1829
|
+
stack({ gap: "md", className: "flex-1" }, [
|
|
1830
|
+
stack({
|
|
1831
|
+
gap: "md",
|
|
1832
|
+
className: "rounded-2xl border bg-white p-6 shadow-sm",
|
|
1833
|
+
style: borderColorStyle("neutral-200")
|
|
1834
|
+
}, [
|
|
1835
|
+
link(
|
|
1836
|
+
{
|
|
1837
|
+
className: "block overflow-hidden rounded-xl border",
|
|
1838
|
+
style: borderColorStyle("neutral-100"),
|
|
1839
|
+
href: { $bind: { from: "post.path" } }
|
|
1840
|
+
},
|
|
1841
|
+
[
|
|
1842
|
+
media(
|
|
1843
|
+
{ className: "h-56 w-full object-cover" },
|
|
1844
|
+
bind("post.image", { transforms: [{ id: "media.fromUrl" }] })
|
|
1845
|
+
)
|
|
1846
|
+
],
|
|
1847
|
+
when("post.image")
|
|
1848
|
+
),
|
|
1849
|
+
stack({ gap: "sm" }, [
|
|
1850
|
+
link(
|
|
1851
|
+
{
|
|
1852
|
+
className: "block text-2xl font-semibold hover:opacity-80",
|
|
1853
|
+
style: textColorStyle("neutral-900"),
|
|
1854
|
+
href: { $bind: { from: "post.path" } }
|
|
1855
|
+
},
|
|
1856
|
+
[text({ as: "span" }, bind("post.title"))]
|
|
1857
|
+
),
|
|
1858
|
+
text(
|
|
1859
|
+
{ as: "p", className: "text-sm", style: textColorStyle("neutral-500") },
|
|
1860
|
+
when("post.publishedAt"),
|
|
1861
|
+
bind("post.publishedAt", { transforms: [{ id: "date.formatShort" }] })
|
|
1862
|
+
),
|
|
1863
|
+
text(
|
|
1864
|
+
{ as: "p", className: "text-base", style: textColorStyle("neutral-600") },
|
|
1865
|
+
bind("post.excerpt", { fallback: "Select a blog post to display its preview." })
|
|
1866
|
+
),
|
|
1867
|
+
link(
|
|
1868
|
+
{
|
|
1869
|
+
className: "inline-flex items-center text-sm font-semibold hover:opacity-80",
|
|
1870
|
+
style: textColorStyle("primary"),
|
|
1871
|
+
href: { $bind: { from: "post.path" } }
|
|
1872
|
+
},
|
|
1873
|
+
[text({ as: "span" }, props({ value: "Read more \u2192" }))]
|
|
1874
|
+
)
|
|
1875
|
+
])
|
|
1876
|
+
], when("post")),
|
|
1877
|
+
stack(
|
|
1878
|
+
{
|
|
1879
|
+
gap: "sm",
|
|
1880
|
+
className: "rounded-xl border border-dashed p-6 text-sm",
|
|
1881
|
+
style: mergeStyles(
|
|
1882
|
+
borderColorStyle("neutral-300"),
|
|
1883
|
+
backgroundColorStyle("neutral-50"),
|
|
1884
|
+
textColorStyle("neutral-500")
|
|
1885
|
+
)
|
|
1886
|
+
},
|
|
1887
|
+
[text({ as: "p" }, props({ value: "Select a blog post to display its preview." }))],
|
|
1888
|
+
when("post", { not: true })
|
|
1889
|
+
)
|
|
1890
|
+
])
|
|
1891
|
+
],
|
|
1892
|
+
{ gap: "lg", className: "w-full md:flex-row md:items-start md:gap-10" }
|
|
1893
|
+
)
|
|
1894
|
+
],
|
|
1895
|
+
data: {
|
|
1896
|
+
key: "post",
|
|
1897
|
+
loader: {
|
|
1898
|
+
endpoint: "getPublishedEntryPreview",
|
|
1899
|
+
params: {
|
|
1900
|
+
type: "post",
|
|
1901
|
+
slug: { $bind: { from: "postSlug" } },
|
|
1902
|
+
siteId: { $bind: { from: "$root.siteId" } }
|
|
1903
|
+
},
|
|
1904
|
+
mode: "server"
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
});
|
|
1908
|
+
|
|
1909
|
+
// ../blocks/src/system/fragments/library/blogListGrid.ts
|
|
1910
|
+
var grid = (props2, children, ...mods) => el("grid", props2 ?? void 0, children ?? void 0, ...mods);
|
|
1911
|
+
var blogListGridFragment = defineFragment({
|
|
1912
|
+
id: "blogListGrid",
|
|
1913
|
+
title: "Blog List Grid",
|
|
1914
|
+
description: "Grid layout for displaying blog posts as cards with images.",
|
|
1915
|
+
fields: [
|
|
1916
|
+
{
|
|
1917
|
+
id: "columns",
|
|
1918
|
+
type: "select",
|
|
1919
|
+
label: "Grid columns",
|
|
1920
|
+
description: "Number of columns in the grid layout.",
|
|
1921
|
+
defaultValue: "3",
|
|
1922
|
+
options: [
|
|
1923
|
+
{ value: "2", label: "2 columns" },
|
|
1924
|
+
{ value: "3", label: "3 columns" },
|
|
1925
|
+
{ value: "4", label: "4 columns" }
|
|
1926
|
+
]
|
|
1927
|
+
},
|
|
1928
|
+
{
|
|
1929
|
+
id: "showImages",
|
|
1930
|
+
type: "boolean",
|
|
1931
|
+
label: "Show images",
|
|
1932
|
+
description: "Display featured images for each post.",
|
|
1933
|
+
defaultValue: true
|
|
1934
|
+
},
|
|
1935
|
+
{
|
|
1936
|
+
id: "showExcerpts",
|
|
1937
|
+
type: "boolean",
|
|
1938
|
+
label: "Show excerpts",
|
|
1939
|
+
description: "Display post preview text.",
|
|
1940
|
+
defaultValue: true
|
|
1941
|
+
},
|
|
1942
|
+
{
|
|
1943
|
+
id: "showDates",
|
|
1944
|
+
type: "boolean",
|
|
1945
|
+
label: "Show dates",
|
|
1946
|
+
description: "Display publish dates.",
|
|
1947
|
+
defaultValue: true
|
|
1948
|
+
}
|
|
1949
|
+
],
|
|
1950
|
+
layout: [
|
|
1951
|
+
// Grid container
|
|
1952
|
+
grid(
|
|
1953
|
+
{
|
|
1954
|
+
className: "grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3"
|
|
1955
|
+
},
|
|
1956
|
+
[
|
|
1957
|
+
// Individual post card
|
|
1958
|
+
stack(
|
|
1959
|
+
{
|
|
1960
|
+
gap: "md",
|
|
1961
|
+
className: "flex flex-col overflow-hidden rounded-xl border shadow-sm transition-shadow hover:shadow-md",
|
|
1962
|
+
style: mergeStyles(
|
|
1963
|
+
borderColorStyle("neutral-200"),
|
|
1964
|
+
backgroundColorStyle("surface")
|
|
1965
|
+
)
|
|
1966
|
+
},
|
|
1967
|
+
[
|
|
1968
|
+
// Featured image
|
|
1969
|
+
link(
|
|
1970
|
+
{
|
|
1971
|
+
className: "block overflow-hidden",
|
|
1972
|
+
href: { $bind: { from: "post.path" } }
|
|
1973
|
+
},
|
|
1974
|
+
[
|
|
1975
|
+
media(
|
|
1976
|
+
{
|
|
1977
|
+
className: "h-56 w-full object-cover transition-transform hover:scale-105"
|
|
1978
|
+
},
|
|
1979
|
+
bind("post.image", { transforms: [{ id: "media.fromUrl" }] })
|
|
1980
|
+
)
|
|
1981
|
+
],
|
|
1982
|
+
when("post.image"),
|
|
1983
|
+
when("showImages")
|
|
1984
|
+
),
|
|
1985
|
+
// Post content
|
|
1986
|
+
stack({ gap: "sm", className: "flex-1 p-6" }, [
|
|
1987
|
+
// Title
|
|
1988
|
+
link(
|
|
1989
|
+
{
|
|
1990
|
+
href: { $bind: { from: "post.path" } },
|
|
1991
|
+
className: "block text-xl font-semibold transition-colors hover:opacity-80",
|
|
1992
|
+
style: textColorStyle("neutral-900")
|
|
1993
|
+
},
|
|
1994
|
+
[text({ as: "h3" }, bind("post.title"))]
|
|
1995
|
+
),
|
|
1996
|
+
// Date
|
|
1997
|
+
text(
|
|
1998
|
+
{
|
|
1999
|
+
as: "time",
|
|
2000
|
+
className: "text-sm",
|
|
2001
|
+
style: textColorStyle("neutral-500")
|
|
2002
|
+
},
|
|
2003
|
+
bind("post.publishedAt", { transforms: [{ id: "date.formatShort" }] }),
|
|
2004
|
+
when("post.publishedAt"),
|
|
2005
|
+
when("showDates")
|
|
2006
|
+
),
|
|
2007
|
+
// Excerpt
|
|
2008
|
+
text(
|
|
2009
|
+
{
|
|
2010
|
+
as: "p",
|
|
2011
|
+
className: "mt-2 text-base line-clamp-3",
|
|
2012
|
+
style: textColorStyle("neutral-600")
|
|
2013
|
+
},
|
|
2014
|
+
bind("post.excerpt"),
|
|
2015
|
+
when("post.excerpt"),
|
|
2016
|
+
when("showExcerpts")
|
|
2017
|
+
),
|
|
2018
|
+
// Read more button
|
|
2019
|
+
button(
|
|
2020
|
+
{
|
|
2021
|
+
href: { $bind: { from: "post.path" } },
|
|
2022
|
+
className: {
|
|
2023
|
+
$bind: { from: "readMoreVariant", fallback: "primary" },
|
|
2024
|
+
$prepend: "button-"
|
|
2025
|
+
}
|
|
2026
|
+
},
|
|
2027
|
+
[text({ as: "span" }, bind("readMoreText", { fallback: "Read more \u2192" }))]
|
|
2028
|
+
)
|
|
2029
|
+
])
|
|
2030
|
+
],
|
|
2031
|
+
repeat("data.posts", "post")
|
|
2032
|
+
)
|
|
2033
|
+
]
|
|
2034
|
+
)
|
|
2035
|
+
],
|
|
2036
|
+
data: {
|
|
2037
|
+
key: "posts",
|
|
2038
|
+
loader: {
|
|
2039
|
+
endpoint: "listPublishedEntries",
|
|
2040
|
+
params: {
|
|
2041
|
+
type: "post",
|
|
2042
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
2043
|
+
limit: { $bind: { from: "postsPerPage", fallback: "12" } },
|
|
2044
|
+
stage: { $bind: { from: "$root.previewStage", fallback: "published" } }
|
|
2045
|
+
},
|
|
2046
|
+
mode: "server"
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
});
|
|
2050
|
+
|
|
2051
|
+
// ../blocks/src/system/fragments/library/blogListStack.ts
|
|
2052
|
+
var blogListStackFragment = defineFragment({
|
|
2053
|
+
id: "blogListStack",
|
|
2054
|
+
title: "Blog List Stack",
|
|
2055
|
+
description: "Vertical list layout for displaying blog posts with optional thumbnails.",
|
|
2056
|
+
fields: [
|
|
2057
|
+
{
|
|
2058
|
+
id: "showImages",
|
|
2059
|
+
type: "boolean",
|
|
2060
|
+
label: "Show images",
|
|
2061
|
+
description: "Display thumbnail images for each post.",
|
|
2062
|
+
defaultValue: true
|
|
2063
|
+
},
|
|
2064
|
+
{
|
|
2065
|
+
id: "showExcerpts",
|
|
2066
|
+
type: "boolean",
|
|
2067
|
+
label: "Show excerpts",
|
|
2068
|
+
description: "Display post preview text.",
|
|
2069
|
+
defaultValue: true
|
|
2070
|
+
},
|
|
2071
|
+
{
|
|
2072
|
+
id: "showDates",
|
|
2073
|
+
type: "boolean",
|
|
2074
|
+
label: "Show dates",
|
|
2075
|
+
description: "Display publish dates.",
|
|
2076
|
+
defaultValue: true
|
|
2077
|
+
}
|
|
2078
|
+
],
|
|
2079
|
+
layout: [
|
|
2080
|
+
// Vertical stack container
|
|
2081
|
+
stack({ gap: "lg", className: "mx-auto max-w-3xl" }, [
|
|
2082
|
+
// Individual post row
|
|
2083
|
+
inline(
|
|
2084
|
+
{
|
|
2085
|
+
gap: "md",
|
|
2086
|
+
className: "items-start border-b pb-6 last:border-b-0",
|
|
2087
|
+
style: borderColorStyle("neutral-200")
|
|
2088
|
+
},
|
|
2089
|
+
[
|
|
2090
|
+
// Optional thumbnail (left-aligned)
|
|
2091
|
+
link(
|
|
2092
|
+
{
|
|
2093
|
+
href: { $bind: { from: "post.path" } },
|
|
2094
|
+
className: "flex-shrink-0"
|
|
2095
|
+
},
|
|
2096
|
+
[
|
|
2097
|
+
media(
|
|
2098
|
+
{
|
|
2099
|
+
className: "h-24 w-24 rounded-lg object-cover transition-transform hover:scale-105"
|
|
2100
|
+
},
|
|
2101
|
+
bind("post.image", { transforms: [{ id: "media.fromUrl" }] })
|
|
2102
|
+
)
|
|
2103
|
+
],
|
|
2104
|
+
when("post.image"),
|
|
2105
|
+
when("showImages")
|
|
2106
|
+
),
|
|
2107
|
+
// Post content (grows to fill space)
|
|
2108
|
+
stack({ gap: "sm", className: "flex-1 min-w-0" }, [
|
|
2109
|
+
// Title
|
|
2110
|
+
link(
|
|
2111
|
+
{
|
|
2112
|
+
href: { $bind: { from: "post.path" } },
|
|
2113
|
+
className: "block text-xl font-semibold transition-colors hover:opacity-80",
|
|
2114
|
+
style: textColorStyle("neutral-900")
|
|
2115
|
+
},
|
|
2116
|
+
[text({ as: "h3" }, bind("post.title"))]
|
|
2117
|
+
),
|
|
2118
|
+
// Date
|
|
2119
|
+
text(
|
|
2120
|
+
{
|
|
2121
|
+
as: "time",
|
|
2122
|
+
className: "text-sm",
|
|
2123
|
+
style: textColorStyle("neutral-500")
|
|
2124
|
+
},
|
|
2125
|
+
bind("post.publishedAt", { transforms: [{ id: "date.formatShort" }] }),
|
|
2126
|
+
when("post.publishedAt"),
|
|
2127
|
+
when("showDates")
|
|
2128
|
+
),
|
|
2129
|
+
// Excerpt
|
|
2130
|
+
text(
|
|
2131
|
+
{
|
|
2132
|
+
as: "p",
|
|
2133
|
+
className: "text-sm line-clamp-2",
|
|
2134
|
+
style: textColorStyle("neutral-600")
|
|
2135
|
+
},
|
|
2136
|
+
bind("post.excerpt"),
|
|
2137
|
+
when("post.excerpt"),
|
|
2138
|
+
when("showExcerpts")
|
|
2139
|
+
),
|
|
2140
|
+
// Read more button
|
|
2141
|
+
button(
|
|
2142
|
+
{
|
|
2143
|
+
href: { $bind: { from: "post.path" } },
|
|
2144
|
+
className: {
|
|
2145
|
+
$bind: { from: "readMoreVariant", fallback: "primary" },
|
|
2146
|
+
$prepend: "button-"
|
|
2147
|
+
}
|
|
2148
|
+
},
|
|
2149
|
+
[text({ as: "span" }, bind("readMoreText", { fallback: "Read more \u2192" }))]
|
|
2150
|
+
)
|
|
2151
|
+
])
|
|
2152
|
+
],
|
|
2153
|
+
repeat("data.posts", "post")
|
|
2154
|
+
)
|
|
2155
|
+
])
|
|
2156
|
+
],
|
|
2157
|
+
data: {
|
|
2158
|
+
key: "posts",
|
|
2159
|
+
loader: {
|
|
2160
|
+
endpoint: "listPublishedEntries",
|
|
2161
|
+
params: {
|
|
2162
|
+
type: "post",
|
|
2163
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
2164
|
+
limit: { $bind: { from: "postsPerPage", fallback: "12" } },
|
|
2165
|
+
stage: { $bind: { from: "$root.previewStage", fallback: "published" } }
|
|
2166
|
+
},
|
|
2167
|
+
mode: "server"
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
});
|
|
2171
|
+
|
|
2172
|
+
// ../blocks/src/system/fragments/library/faqHeading.ts
|
|
2173
|
+
var faqHeadingFragment = defineFragment({
|
|
2174
|
+
id: "faqHeading",
|
|
2175
|
+
title: "FAQ Heading",
|
|
2176
|
+
description: "Optional eyebrow, title, and description for FAQ sections.",
|
|
2177
|
+
fields: [
|
|
2178
|
+
{
|
|
2179
|
+
id: "eyebrow",
|
|
2180
|
+
type: "text",
|
|
2181
|
+
label: "Eyebrow",
|
|
2182
|
+
description: "Short label displayed above the title.",
|
|
2183
|
+
visibleRoles: ["designer", "admin"],
|
|
2184
|
+
maxLength: 60
|
|
2185
|
+
},
|
|
2186
|
+
{
|
|
2187
|
+
id: "title",
|
|
2188
|
+
type: "text",
|
|
2189
|
+
label: "Title",
|
|
2190
|
+
required: false,
|
|
2191
|
+
maxLength: 120
|
|
2192
|
+
},
|
|
2193
|
+
{
|
|
2194
|
+
id: "description",
|
|
2195
|
+
type: "richText",
|
|
2196
|
+
label: "Description",
|
|
2197
|
+
required: false,
|
|
2198
|
+
ui: { variant: "limited" }
|
|
2199
|
+
}
|
|
2200
|
+
],
|
|
2201
|
+
layout: stack(
|
|
2202
|
+
{
|
|
2203
|
+
gap: "sm",
|
|
2204
|
+
className: "mx-auto max-w-3xl text-center mb-4"
|
|
2205
|
+
},
|
|
2206
|
+
[
|
|
2207
|
+
text(
|
|
2208
|
+
{
|
|
2209
|
+
as: "p",
|
|
2210
|
+
className: "faq-eyebrow text-sm font-semibold uppercase tracking-wide",
|
|
2211
|
+
style: textColorStyle("primary")
|
|
2212
|
+
},
|
|
2213
|
+
when("content.eyebrow"),
|
|
2214
|
+
bind("content.eyebrow")
|
|
2215
|
+
),
|
|
2216
|
+
text(
|
|
2217
|
+
{
|
|
2218
|
+
as: "h2",
|
|
2219
|
+
className: "faq-title text-3xl font-semibold sm:text-4xl",
|
|
2220
|
+
style: textColorStyle("neutral-900")
|
|
2221
|
+
},
|
|
2222
|
+
when("content.title"),
|
|
2223
|
+
bind("content.title")
|
|
2224
|
+
),
|
|
2225
|
+
richText(
|
|
2226
|
+
{
|
|
2227
|
+
className: "faq-description prose mx-auto max-w-none text-base sm:text-lg",
|
|
2228
|
+
style: textColorStyle("neutral-600")
|
|
2229
|
+
},
|
|
2230
|
+
when("content.description"),
|
|
2231
|
+
bind("content.description")
|
|
2232
|
+
)
|
|
2233
|
+
]
|
|
2234
|
+
)
|
|
2235
|
+
});
|
|
2236
|
+
|
|
2237
|
+
// ../blocks/src/system/fragments/library/faqAccordion.ts
|
|
2238
|
+
var faqAccordionFragment = defineFragment({
|
|
2239
|
+
id: "faqAccordion",
|
|
2240
|
+
title: "FAQ Accordion",
|
|
2241
|
+
description: "Expandable list of question and answer pairs.",
|
|
2242
|
+
fields: [
|
|
2243
|
+
{
|
|
2244
|
+
id: "items",
|
|
2245
|
+
type: "repeater",
|
|
2246
|
+
label: "FAQ items",
|
|
2247
|
+
itemLabel: "FAQ item",
|
|
2248
|
+
itemLabelSource: "question",
|
|
2249
|
+
minItems: 1,
|
|
2250
|
+
maxItems: 30,
|
|
2251
|
+
schema: {
|
|
2252
|
+
fields: [
|
|
2253
|
+
{
|
|
2254
|
+
id: "question",
|
|
2255
|
+
type: "text",
|
|
2256
|
+
label: "Question",
|
|
2257
|
+
required: true,
|
|
2258
|
+
maxLength: 160
|
|
2259
|
+
},
|
|
2260
|
+
{
|
|
2261
|
+
id: "answer",
|
|
2262
|
+
type: "richText",
|
|
2263
|
+
label: "Answer",
|
|
2264
|
+
required: true,
|
|
2265
|
+
ui: { variant: "limited" }
|
|
2266
|
+
}
|
|
2267
|
+
]
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
],
|
|
2271
|
+
layout: accordionList({
|
|
2272
|
+
collection: "content.items",
|
|
2273
|
+
itemName: "faqItem",
|
|
2274
|
+
indexName: "faqIndex",
|
|
2275
|
+
accordionProps: {
|
|
2276
|
+
className: "accordion-root",
|
|
2277
|
+
// CSS-first: all styling handled by theme CSS
|
|
2278
|
+
type: "single",
|
|
2279
|
+
collapsible: true
|
|
2280
|
+
},
|
|
2281
|
+
itemProps: {
|
|
2282
|
+
className: "accordion-item",
|
|
2283
|
+
iconStyle: { $bind: { from: "theme.accordions.icon.style" } }
|
|
2284
|
+
// Pass icon style from theme
|
|
2285
|
+
},
|
|
2286
|
+
triggerFrom: "faqItem.question",
|
|
2287
|
+
contentFrom: "faqItem.answer"
|
|
2288
|
+
})
|
|
2289
|
+
});
|
|
2290
|
+
|
|
2291
|
+
// ../blocks/src/system/fragments/library/card.ts
|
|
2292
|
+
var cardFragment = defineFragment({
|
|
2293
|
+
id: "card",
|
|
2294
|
+
title: "Card",
|
|
2295
|
+
description: "Content card with optional media, title, body, and action buttons",
|
|
2296
|
+
category: "content",
|
|
2297
|
+
icon: "LayoutCard",
|
|
2298
|
+
fields: [
|
|
2299
|
+
{
|
|
2300
|
+
id: "media",
|
|
2301
|
+
type: "media",
|
|
2302
|
+
label: "Featured media",
|
|
2303
|
+
description: "Image or video displayed at top of card",
|
|
2304
|
+
required: false,
|
|
2305
|
+
mediaKinds: ["image", "video"]
|
|
2306
|
+
},
|
|
2307
|
+
{
|
|
2308
|
+
id: "aspectRatio",
|
|
2309
|
+
type: "select",
|
|
2310
|
+
label: "Media aspect ratio",
|
|
2311
|
+
defaultValue: "auto",
|
|
2312
|
+
options: [
|
|
2313
|
+
{ value: "auto", label: "Auto" },
|
|
2314
|
+
{ value: "16/9", label: "16:9 (Landscape)" },
|
|
2315
|
+
{ value: "4/3", label: "4:3 (Standard)" },
|
|
2316
|
+
{ value: "1/1", label: "1:1 (Square)" },
|
|
2317
|
+
{ value: "3/4", label: "3:4 (Portrait)" }
|
|
2318
|
+
],
|
|
2319
|
+
condition: {
|
|
2320
|
+
field: "media",
|
|
2321
|
+
operator: "isNotEmpty"
|
|
2322
|
+
}
|
|
2323
|
+
},
|
|
2324
|
+
{
|
|
2325
|
+
id: "title",
|
|
2326
|
+
type: "text",
|
|
2327
|
+
label: "Title",
|
|
2328
|
+
required: true,
|
|
2329
|
+
maxLength: 120
|
|
2330
|
+
},
|
|
2331
|
+
{
|
|
2332
|
+
id: "body",
|
|
2333
|
+
type: "richText",
|
|
2334
|
+
label: "Body",
|
|
2335
|
+
description: "Main card content",
|
|
2336
|
+
format: "markdown"
|
|
2337
|
+
},
|
|
2338
|
+
createCtasRepeater({
|
|
2339
|
+
id: "ctas",
|
|
2340
|
+
label: "Action buttons",
|
|
2341
|
+
itemLabel: "Button",
|
|
2342
|
+
minItems: 0,
|
|
2343
|
+
maxItems: 3
|
|
2344
|
+
}),
|
|
2345
|
+
{
|
|
2346
|
+
id: "variant",
|
|
2347
|
+
type: "select",
|
|
2348
|
+
label: "Card variant",
|
|
2349
|
+
description: "Choose a card style from your theme",
|
|
2350
|
+
required: false,
|
|
2351
|
+
defaultValue: "default",
|
|
2352
|
+
options: [
|
|
2353
|
+
{ value: "default", label: "Default" },
|
|
2354
|
+
{ value: "variant1", label: "Variant 1" },
|
|
2355
|
+
{ value: "variant2", label: "Variant 2" }
|
|
2356
|
+
]
|
|
2357
|
+
}
|
|
2358
|
+
],
|
|
2359
|
+
layout: stack(
|
|
2360
|
+
{
|
|
2361
|
+
gap: "none",
|
|
2362
|
+
className: {
|
|
2363
|
+
$bind: {
|
|
2364
|
+
from: "variant",
|
|
2365
|
+
fallback: "default"
|
|
2366
|
+
},
|
|
2367
|
+
$prepend: "card-"
|
|
2368
|
+
}
|
|
2369
|
+
},
|
|
2370
|
+
[
|
|
2371
|
+
// Featured media at top (conditional) - full width, no padding
|
|
2372
|
+
// CSS handles corner radius via .card-{variant} .card-media selector
|
|
2373
|
+
media(
|
|
2374
|
+
{
|
|
2375
|
+
className: "card-media w-full h-auto object-cover",
|
|
2376
|
+
style: {
|
|
2377
|
+
aspectRatio: { $bind: { from: "aspectRatio" } }
|
|
2378
|
+
}
|
|
2379
|
+
},
|
|
2380
|
+
when("media"),
|
|
2381
|
+
bind("media")
|
|
2382
|
+
),
|
|
2383
|
+
// Content wrapper with padding based on variant
|
|
2384
|
+
// CSS handles padding via .card-{variant} .card-content selector
|
|
2385
|
+
stack(
|
|
2386
|
+
{
|
|
2387
|
+
gap: "sm",
|
|
2388
|
+
className: "card-content"
|
|
2389
|
+
},
|
|
2390
|
+
[
|
|
2391
|
+
text(
|
|
2392
|
+
{
|
|
2393
|
+
as: "h3",
|
|
2394
|
+
className: "card-title text-xl font-semibold"
|
|
2395
|
+
},
|
|
2396
|
+
bind("title")
|
|
2397
|
+
),
|
|
2398
|
+
richText(
|
|
2399
|
+
{
|
|
2400
|
+
className: "card-body text-base",
|
|
2401
|
+
style: textColorStyle("mutedText")
|
|
2402
|
+
},
|
|
2403
|
+
when("body"),
|
|
2404
|
+
bind("body")
|
|
2405
|
+
),
|
|
2406
|
+
// Action buttons at bottom (conditional)
|
|
2407
|
+
ctaRow({
|
|
2408
|
+
collectionPath: "ctas",
|
|
2409
|
+
itemName: "cta",
|
|
2410
|
+
gap: "sm",
|
|
2411
|
+
justify: "start",
|
|
2412
|
+
containerClassName: "mt-2"
|
|
2413
|
+
})
|
|
2414
|
+
]
|
|
2415
|
+
)
|
|
2416
|
+
]
|
|
2417
|
+
)
|
|
2418
|
+
});
|
|
2419
|
+
|
|
2420
|
+
// ../blocks/src/system/fragments/library/heading.ts
|
|
2421
|
+
var headingFragment = defineFragment({
|
|
2422
|
+
id: "heading",
|
|
2423
|
+
title: "Heading",
|
|
2424
|
+
description: "Text heading",
|
|
2425
|
+
category: "content",
|
|
2426
|
+
icon: "Heading",
|
|
2427
|
+
fields: [
|
|
2428
|
+
{
|
|
2429
|
+
id: "text",
|
|
2430
|
+
type: "text",
|
|
2431
|
+
label: "Heading text",
|
|
2432
|
+
required: true,
|
|
2433
|
+
maxLength: 200
|
|
2434
|
+
},
|
|
2435
|
+
{
|
|
2436
|
+
id: "level",
|
|
2437
|
+
type: "select",
|
|
2438
|
+
label: "Heading level",
|
|
2439
|
+
defaultValue: "h2",
|
|
2440
|
+
options: [
|
|
2441
|
+
{ value: "h1", label: "H1 (Large)" },
|
|
2442
|
+
{ value: "h2", label: "H2 (Medium)" },
|
|
2443
|
+
{ value: "h3", label: "H3 (Small)" }
|
|
2444
|
+
]
|
|
2445
|
+
}
|
|
2446
|
+
],
|
|
2447
|
+
layout: text(
|
|
2448
|
+
{
|
|
2449
|
+
as: { $bind: { from: "level" } },
|
|
2450
|
+
// Relative path - resolves to current scope
|
|
2451
|
+
className: "fragment-heading font-bold"
|
|
2452
|
+
},
|
|
2453
|
+
bind("text")
|
|
2454
|
+
// Relative path - resolves to current scope
|
|
2455
|
+
)
|
|
2456
|
+
});
|
|
2457
|
+
|
|
2458
|
+
// ../blocks/src/system/fragments/library/richText.ts
|
|
2459
|
+
var richTextFragment = defineFragment({
|
|
2460
|
+
id: "richText",
|
|
2461
|
+
title: "Rich Text",
|
|
2462
|
+
description: "Formatted text with markdown",
|
|
2463
|
+
category: "content",
|
|
2464
|
+
icon: "FileText",
|
|
2465
|
+
fields: [
|
|
2466
|
+
{
|
|
2467
|
+
id: "content",
|
|
2468
|
+
type: "richText",
|
|
2469
|
+
label: "Content",
|
|
2470
|
+
required: true,
|
|
2471
|
+
format: "markdown"
|
|
2472
|
+
}
|
|
2473
|
+
],
|
|
2474
|
+
layout: richText(
|
|
2475
|
+
{
|
|
2476
|
+
className: "fragment-richtext prose prose-neutral"
|
|
2477
|
+
},
|
|
2478
|
+
bind("content")
|
|
2479
|
+
// Relative path - resolves to current scope
|
|
2480
|
+
)
|
|
2481
|
+
});
|
|
2482
|
+
|
|
2483
|
+
// ../blocks/src/system/fragments/utils/toRepeaterSchema.ts
|
|
2484
|
+
function fragmentsToRepeaterField(id, label, fragments, options = {}) {
|
|
2485
|
+
return {
|
|
2486
|
+
id,
|
|
2487
|
+
type: "repeater",
|
|
2488
|
+
label,
|
|
2489
|
+
description: options.description,
|
|
2490
|
+
polymorphic: true,
|
|
2491
|
+
itemLabel: options.itemLabel ?? "Item",
|
|
2492
|
+
itemLabelSource: "_type",
|
|
2493
|
+
minItems: options.minItems ?? 0,
|
|
2494
|
+
maxItems: options.maxItems,
|
|
2495
|
+
allowConversion: options.allowConversion ?? true,
|
|
2496
|
+
required: false,
|
|
2497
|
+
itemTypes: Object.fromEntries(
|
|
2498
|
+
Object.entries(fragments).map(([typeId, fragment]) => [
|
|
2499
|
+
typeId,
|
|
2500
|
+
{
|
|
2501
|
+
label: fragment.title ?? typeId,
|
|
2502
|
+
icon: fragment.icon,
|
|
2503
|
+
fields: fragment.fields
|
|
2504
|
+
}
|
|
2505
|
+
])
|
|
2506
|
+
)
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
// ../blocks/src/system/fragments/library/image.ts
|
|
2511
|
+
var imageFragment = defineFragment({
|
|
2512
|
+
id: "image",
|
|
2513
|
+
title: "Image",
|
|
2514
|
+
description: "Image with optional caption",
|
|
2515
|
+
category: "content",
|
|
2516
|
+
icon: "Image",
|
|
2517
|
+
fields: [
|
|
2518
|
+
{
|
|
2519
|
+
id: "image",
|
|
2520
|
+
type: "media",
|
|
2521
|
+
label: "Image",
|
|
2522
|
+
required: true,
|
|
2523
|
+
mediaKinds: ["image"]
|
|
2524
|
+
},
|
|
2525
|
+
{
|
|
2526
|
+
id: "caption",
|
|
2527
|
+
type: "text",
|
|
2528
|
+
label: "Caption",
|
|
2529
|
+
required: false,
|
|
2530
|
+
maxLength: 200
|
|
2531
|
+
},
|
|
2532
|
+
{
|
|
2533
|
+
id: "aspectRatio",
|
|
2534
|
+
type: "select",
|
|
2535
|
+
label: "Aspect ratio",
|
|
2536
|
+
defaultValue: "auto",
|
|
2537
|
+
options: [
|
|
2538
|
+
{ value: "auto", label: "Auto" },
|
|
2539
|
+
{ value: "16/9", label: "16:9 (Landscape)" },
|
|
2540
|
+
{ value: "4/3", label: "4:3 (Standard)" },
|
|
2541
|
+
{ value: "1/1", label: "1:1 (Square)" },
|
|
2542
|
+
{ value: "3/4", label: "3:4 (Portrait)" }
|
|
2543
|
+
]
|
|
2544
|
+
}
|
|
2545
|
+
],
|
|
2546
|
+
layout: stack({ gap: "sm" }, [
|
|
2547
|
+
media(
|
|
2548
|
+
{
|
|
2549
|
+
className: "fragment-image w-full h-auto object-cover",
|
|
2550
|
+
style: {
|
|
2551
|
+
aspectRatio: { $bind: { from: "aspectRatio" } }
|
|
2552
|
+
}
|
|
2553
|
+
},
|
|
2554
|
+
bind("image")
|
|
2555
|
+
),
|
|
2556
|
+
text(
|
|
2557
|
+
{
|
|
2558
|
+
as: "p",
|
|
2559
|
+
className: "image-caption text-sm text-center",
|
|
2560
|
+
style: textColorStyle("mutedText")
|
|
2561
|
+
},
|
|
2562
|
+
when("caption"),
|
|
2563
|
+
bind("caption")
|
|
2564
|
+
)
|
|
2565
|
+
])
|
|
2566
|
+
});
|
|
2567
|
+
|
|
2568
|
+
// ../blocks/src/system/fragments/library/columnContent.ts
|
|
2569
|
+
var columnContentFragment = defineFragment({
|
|
2570
|
+
id: "columnContent",
|
|
2571
|
+
title: "Column",
|
|
2572
|
+
description: "A single column with customizable content",
|
|
2573
|
+
category: "layout",
|
|
2574
|
+
icon: "RectangleVertical",
|
|
2575
|
+
fields: [
|
|
2576
|
+
fragmentsToRepeaterField(
|
|
2577
|
+
"items",
|
|
2578
|
+
"Column items",
|
|
2579
|
+
{
|
|
2580
|
+
image: imageFragment,
|
|
2581
|
+
heading: headingFragment,
|
|
2582
|
+
richText: richTextFragment
|
|
2583
|
+
},
|
|
2584
|
+
{
|
|
2585
|
+
minItems: 0,
|
|
2586
|
+
maxItems: 20,
|
|
2587
|
+
itemLabel: "Item",
|
|
2588
|
+
description: "Add content to this column"
|
|
2589
|
+
}
|
|
2590
|
+
)
|
|
2591
|
+
],
|
|
2592
|
+
layout: stack(
|
|
2593
|
+
{ gap: "md", className: "h-full" },
|
|
2594
|
+
[
|
|
2595
|
+
{
|
|
2596
|
+
type: "stack",
|
|
2597
|
+
gap: "md",
|
|
2598
|
+
children: typeBasedLayout(
|
|
2599
|
+
{
|
|
2600
|
+
image: imageFragment.layout,
|
|
2601
|
+
heading: headingFragment.layout,
|
|
2602
|
+
richText: richTextFragment.layout
|
|
2603
|
+
},
|
|
2604
|
+
{ itemName: "item" }
|
|
2605
|
+
),
|
|
2606
|
+
$repeat: {
|
|
2607
|
+
collection: { from: "items" },
|
|
2608
|
+
itemName: "item"
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
]
|
|
2612
|
+
)
|
|
2613
|
+
});
|
|
2614
|
+
|
|
2615
|
+
// ../blocks/src/system/constants/background.ts
|
|
2616
|
+
var BACKGROUND_POSITION_PRESETS = [
|
|
2617
|
+
{ value: "center", label: "Center" },
|
|
2618
|
+
{ value: "top", label: "Top" },
|
|
2619
|
+
{ value: "top left", label: "Top Left" },
|
|
2620
|
+
{ value: "top right", label: "Top Right" },
|
|
2621
|
+
{ value: "bottom", label: "Bottom" },
|
|
2622
|
+
{ value: "bottom left", label: "Bottom Left" },
|
|
2623
|
+
{ value: "bottom right", label: "Bottom Right" },
|
|
2624
|
+
{ value: "left", label: "Left" },
|
|
2625
|
+
{ value: "right", label: "Right" },
|
|
2626
|
+
// The following are valid keywords but are not included in the UI to reduce clutter.
|
|
2627
|
+
// They are included here to ensure the transform logic can correctly identify them as presets.
|
|
2628
|
+
{ value: "top center", label: "Top Center" },
|
|
2629
|
+
{ value: "bottom center", label: "Bottom Center" },
|
|
2630
|
+
{ value: "left center", label: "Left Center" },
|
|
2631
|
+
{ value: "right center", label: "Right Center" },
|
|
2632
|
+
{ value: "left top", label: "Left Top" },
|
|
2633
|
+
{ value: "right top", label: "Right Top" },
|
|
2634
|
+
{ value: "left bottom", label: "Left Bottom" },
|
|
2635
|
+
{ value: "right bottom", label: "Right Bottom" }
|
|
2636
|
+
];
|
|
2637
|
+
BACKGROUND_POSITION_PRESETS.map((p) => p.value);
|
|
2638
|
+
|
|
2639
|
+
// ../blocks/src/system/fields/background.ts
|
|
2640
|
+
function createBackgroundField(options = {}) {
|
|
2641
|
+
const {
|
|
2642
|
+
id = "background",
|
|
2643
|
+
label = "Background",
|
|
2644
|
+
allowColor = true,
|
|
2645
|
+
allowGradient = true,
|
|
2646
|
+
allowImage = true,
|
|
2647
|
+
includeAdvanced = false
|
|
2648
|
+
} = options;
|
|
2649
|
+
const tabs = [];
|
|
2650
|
+
if (allowColor) {
|
|
2651
|
+
tabs.push({
|
|
2652
|
+
id: "color",
|
|
2653
|
+
label: "Color",
|
|
2654
|
+
description: "Solid color background using theme tokens or custom colors",
|
|
2655
|
+
sdkSectionOption: "backgroundColor",
|
|
2656
|
+
fields: [
|
|
2657
|
+
{
|
|
2658
|
+
id: "color",
|
|
2659
|
+
type: "text",
|
|
2660
|
+
label: "Color",
|
|
2661
|
+
description: "Select a background color from the available options.",
|
|
2662
|
+
required: false,
|
|
2663
|
+
multiline: false,
|
|
2664
|
+
ui: {
|
|
2665
|
+
// Use BackgroundColorWidget via widget override
|
|
2666
|
+
widget: "backgroundColor"
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
]
|
|
2670
|
+
});
|
|
2671
|
+
}
|
|
2672
|
+
if (allowGradient) {
|
|
2673
|
+
tabs.push({
|
|
2674
|
+
id: "gradient",
|
|
2675
|
+
label: "Gradient",
|
|
2676
|
+
description: "CSS gradient background",
|
|
2677
|
+
sdkSectionOption: "backgroundGradient",
|
|
2678
|
+
fields: [
|
|
2679
|
+
{
|
|
2680
|
+
id: "gradient",
|
|
2681
|
+
type: "text",
|
|
2682
|
+
label: "Gradient",
|
|
2683
|
+
description: "CSS gradient value (e.g., linear-gradient(to right, #ff0000, #00ff00)).",
|
|
2684
|
+
required: false,
|
|
2685
|
+
multiline: true,
|
|
2686
|
+
ui: {
|
|
2687
|
+
placeholder: "linear-gradient(to right, #ff0000, #00ff00)"
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
]
|
|
2691
|
+
});
|
|
2692
|
+
}
|
|
2693
|
+
if (allowImage) {
|
|
2694
|
+
const imageFields = [
|
|
2695
|
+
{
|
|
2696
|
+
id: "image",
|
|
2697
|
+
type: "media",
|
|
2698
|
+
label: "Image",
|
|
2699
|
+
description: "Background image file",
|
|
2700
|
+
mediaKinds: ["image"],
|
|
2701
|
+
required: false
|
|
2702
|
+
}
|
|
2703
|
+
];
|
|
2704
|
+
if (includeAdvanced) {
|
|
2705
|
+
const objectFitField = {
|
|
2706
|
+
id: "objectFit",
|
|
2707
|
+
type: "select",
|
|
2708
|
+
label: "Image Sizing",
|
|
2709
|
+
description: "How the image should be sized and positioned.",
|
|
2710
|
+
required: false,
|
|
2711
|
+
multiple: false,
|
|
2712
|
+
options: [
|
|
2713
|
+
{ value: "fill", label: "Fill (cover)" },
|
|
2714
|
+
{ value: "fit", label: "Fit (contain)" },
|
|
2715
|
+
{ value: "original", label: "Original size" },
|
|
2716
|
+
{ value: "custom", label: "Scale up" }
|
|
2717
|
+
]
|
|
2718
|
+
};
|
|
2719
|
+
const scaleField = {
|
|
2720
|
+
id: "scale",
|
|
2721
|
+
type: "presetOrCustom",
|
|
2722
|
+
label: "Scale",
|
|
2723
|
+
description: "Image scale amount.",
|
|
2724
|
+
required: false,
|
|
2725
|
+
ui: {
|
|
2726
|
+
visibleWhen: {
|
|
2727
|
+
field: "objectFit",
|
|
2728
|
+
equals: "custom"
|
|
2729
|
+
}
|
|
2730
|
+
},
|
|
2731
|
+
presets: [
|
|
2732
|
+
{ value: "125", label: "125%" },
|
|
2733
|
+
{ value: "150", label: "150%" },
|
|
2734
|
+
{ value: "175", label: "175%" },
|
|
2735
|
+
{ value: "200", label: "200%" }
|
|
2736
|
+
],
|
|
2737
|
+
customInput: {
|
|
2738
|
+
placeholder: "e.g., 800px, 50vw, 175%",
|
|
2739
|
+
helpText: "Enter a custom size using %, px, vh, vw, rem, or em units"
|
|
2740
|
+
}
|
|
2741
|
+
};
|
|
2742
|
+
const positionField = {
|
|
2743
|
+
id: "position",
|
|
2744
|
+
type: "presetOrCustom",
|
|
2745
|
+
label: "Position",
|
|
2746
|
+
description: 'Anchor point for the image. Relevant for "Fill" and "Custom size" options.',
|
|
2747
|
+
required: false,
|
|
2748
|
+
presets: [...BACKGROUND_POSITION_PRESETS],
|
|
2749
|
+
customInput: {
|
|
2750
|
+
placeholder: "e.g., 25% 75%, top 20px left 50px",
|
|
2751
|
+
helpText: "Enter a custom CSS background-position value"
|
|
2752
|
+
}
|
|
2753
|
+
};
|
|
2754
|
+
const opacityField = {
|
|
2755
|
+
id: "opacity",
|
|
2756
|
+
type: "number",
|
|
2757
|
+
label: "Opacity",
|
|
2758
|
+
description: "Image opacity (0-100).",
|
|
2759
|
+
required: false,
|
|
2760
|
+
ui: {
|
|
2761
|
+
min: 0,
|
|
2762
|
+
max: 100,
|
|
2763
|
+
step: 5
|
|
2764
|
+
}
|
|
2765
|
+
};
|
|
2766
|
+
imageFields.push(
|
|
2767
|
+
objectFitField,
|
|
2768
|
+
scaleField,
|
|
2769
|
+
positionField,
|
|
2770
|
+
opacityField
|
|
2771
|
+
);
|
|
2772
|
+
}
|
|
2773
|
+
tabs.push({
|
|
2774
|
+
id: "image",
|
|
2775
|
+
label: "Image",
|
|
2776
|
+
description: includeAdvanced ? "Background image with advanced controls" : "Background image",
|
|
2777
|
+
sdkSectionOption: "backgroundImage",
|
|
2778
|
+
fields: imageFields
|
|
2779
|
+
});
|
|
2780
|
+
}
|
|
2781
|
+
return {
|
|
2782
|
+
id,
|
|
2783
|
+
type: "tabGroup",
|
|
2784
|
+
label,
|
|
2785
|
+
required: false,
|
|
2786
|
+
tabs,
|
|
2787
|
+
activeTabField: "type",
|
|
2788
|
+
ui: {
|
|
2789
|
+
hideLabel: false
|
|
2790
|
+
}
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
// ../blocks/src/system/fields/boxStyles.ts
|
|
2795
|
+
function sectionStylesField(options = {}) {
|
|
2796
|
+
const {
|
|
2797
|
+
id = "_sectionStyles",
|
|
2798
|
+
label = "Section styles",
|
|
2799
|
+
includeBackground = true
|
|
2800
|
+
} = options;
|
|
2801
|
+
const fields4 = [];
|
|
2802
|
+
if (includeBackground) {
|
|
2803
|
+
fields4.push(
|
|
2804
|
+
createBackgroundField({
|
|
2805
|
+
id: "background",
|
|
2806
|
+
label: "Background",
|
|
2807
|
+
allowColor: true,
|
|
2808
|
+
allowGradient: true,
|
|
2809
|
+
allowImage: true,
|
|
2810
|
+
includeAdvanced: true
|
|
2811
|
+
})
|
|
2812
|
+
);
|
|
2813
|
+
}
|
|
2814
|
+
fields4.push({
|
|
2815
|
+
id: "spacing",
|
|
2816
|
+
type: "select",
|
|
2817
|
+
label: "Inner spacing",
|
|
2818
|
+
description: "Vertical padding for the section.",
|
|
2819
|
+
required: false,
|
|
2820
|
+
multiple: false,
|
|
2821
|
+
options: [
|
|
2822
|
+
{ value: "none", label: "None" },
|
|
2823
|
+
{ value: "compact", label: "Compact" },
|
|
2824
|
+
{ value: "cozy", label: "Cozy" },
|
|
2825
|
+
{ value: "medium", label: "Medium" },
|
|
2826
|
+
{ value: "comfortable", label: "Comfortable" },
|
|
2827
|
+
{ value: "spacious", label: "Spacious" }
|
|
2828
|
+
]
|
|
2829
|
+
});
|
|
2830
|
+
return {
|
|
2831
|
+
id,
|
|
2832
|
+
type: "modal",
|
|
2833
|
+
label,
|
|
2834
|
+
required: false,
|
|
2835
|
+
schema: { fields: fields4 },
|
|
2836
|
+
ui: {
|
|
2837
|
+
modalConfig: {
|
|
2838
|
+
buttonLabel: label,
|
|
2839
|
+
description: "Configure background and spacing for this section.",
|
|
2840
|
+
buttonVariant: "outline",
|
|
2841
|
+
showCustomizedIndicator: true
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
// ../blocks/src/system/defineBlock.ts
|
|
2848
|
+
function createBlockManifest(config) {
|
|
2849
|
+
const composition2 = config.fragments ? composeFragments(config.fragments) : { fields: []};
|
|
2850
|
+
const allFields = [
|
|
2851
|
+
...composition2.fields,
|
|
2852
|
+
...config.additionalFields ?? []
|
|
2853
|
+
];
|
|
2854
|
+
if (!config.skipSectionStyles) {
|
|
2855
|
+
allFields.push(
|
|
2856
|
+
sectionStylesField({
|
|
2857
|
+
id: "_sectionStyles",
|
|
2858
|
+
label: "Section styles"
|
|
2859
|
+
})
|
|
2860
|
+
);
|
|
2861
|
+
}
|
|
2862
|
+
const fields4 = fieldSchema.array().parse(allFields);
|
|
2863
|
+
const layout = config.layout;
|
|
2864
|
+
const variants = config.variants;
|
|
2865
|
+
let behaviours = config.behaviours;
|
|
2866
|
+
if (!behaviours && config.paletteHidden !== void 0) {
|
|
2867
|
+
behaviours = {
|
|
2868
|
+
supportsThemeSwitching: true,
|
|
2869
|
+
inlineEditing: true,
|
|
2870
|
+
animation: true,
|
|
2871
|
+
paletteHidden: config.paletteHidden
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
const manifest = {
|
|
2875
|
+
name: config.id,
|
|
2876
|
+
version: "0.1.0",
|
|
2877
|
+
title: config.title,
|
|
2878
|
+
titleSource: config.titleSource,
|
|
2879
|
+
description: config.description ?? "",
|
|
2880
|
+
component: config.component ?? deriveComponentName(config.id),
|
|
2881
|
+
fields: fields4,
|
|
2882
|
+
slots: [],
|
|
2883
|
+
// Always empty
|
|
2884
|
+
styleTokens: config.styleTokens,
|
|
2885
|
+
behaviours,
|
|
2886
|
+
category: config.category,
|
|
2887
|
+
contentTypes: config.contentTypes,
|
|
2888
|
+
tags: config.tags ?? [],
|
|
2889
|
+
icon: config.icon ?? "Box",
|
|
2890
|
+
layout,
|
|
2891
|
+
variants,
|
|
2892
|
+
defaultVariant: config.defaultVariant
|
|
2893
|
+
};
|
|
2894
|
+
return augmentManifest(manifest);
|
|
2895
|
+
}
|
|
2896
|
+
function deriveComponentName(id) {
|
|
2897
|
+
const base = id.replace(/^block\./, "");
|
|
2898
|
+
const dotSeparated = base.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase();
|
|
2899
|
+
return `${dotSeparated}.default`;
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
// ../blocks/src/system/blocks/hero.ts
|
|
2903
|
+
var heroCopyAndCta = composeFragments([
|
|
2904
|
+
{ fragment: heroCopyFragment },
|
|
2905
|
+
{ fragment: ctaRowFragment }
|
|
2906
|
+
]);
|
|
2907
|
+
var heroContentNodes = heroCopyAndCta.layout;
|
|
2908
|
+
var classicLayout = styledSection({
|
|
2909
|
+
children: sectionContainer(heroContentNodes, {
|
|
2910
|
+
gap: "md",
|
|
2911
|
+
className: "text-center"
|
|
2912
|
+
})
|
|
2913
|
+
});
|
|
2914
|
+
var microLayout = styledSection({
|
|
2915
|
+
children: sectionContainer(heroContentNodes, {
|
|
2916
|
+
gap: "sm",
|
|
2917
|
+
className: "text-center"
|
|
2918
|
+
}),
|
|
2919
|
+
spacing: "compact"
|
|
2920
|
+
});
|
|
2921
|
+
var splitLayoutBase = styledSection({
|
|
2922
|
+
children: {
|
|
2923
|
+
type: "grid",
|
|
2924
|
+
cols: 1,
|
|
2925
|
+
colsSm: 2,
|
|
2926
|
+
gap: "lg",
|
|
2927
|
+
className: "mx-auto max-w-7xl items-center",
|
|
2928
|
+
children: [
|
|
2929
|
+
{
|
|
2930
|
+
type: "stack",
|
|
2931
|
+
gap: "md",
|
|
2932
|
+
className: "text-left",
|
|
2933
|
+
children: heroContentNodes
|
|
2934
|
+
}
|
|
2935
|
+
]
|
|
2936
|
+
}
|
|
2937
|
+
});
|
|
2938
|
+
var splitLayout = splitLayoutBase;
|
|
2939
|
+
var splitReverseLayout = splitLayoutBase;
|
|
2940
|
+
var heroManifest = createBlockManifest({
|
|
2941
|
+
id: "block.hero",
|
|
2942
|
+
title: "Hero",
|
|
2943
|
+
titleSource: "headline",
|
|
2944
|
+
category: "layout",
|
|
2945
|
+
fragments: [
|
|
2946
|
+
{ fragment: heroCopyFragment, fieldPriority: 0 },
|
|
2947
|
+
{ fragment: ctaRowFragment, fieldPriority: 2 }
|
|
2948
|
+
],
|
|
2949
|
+
layout: classicLayout,
|
|
2950
|
+
variants: {
|
|
2951
|
+
classic: classicLayout,
|
|
2952
|
+
micro: microLayout,
|
|
2953
|
+
split: splitLayout,
|
|
2954
|
+
splitReverse: splitReverseLayout
|
|
2955
|
+
},
|
|
2956
|
+
defaultVariant: "classic",
|
|
2957
|
+
description: "Hero section with headline, subtitle, and repeatable CTAs.",
|
|
2958
|
+
tags: ["header", "banner", "landing", "introduction", "welcome", "splash", "headline"],
|
|
2959
|
+
icon: "Sparkles",
|
|
2960
|
+
styleTokens: {
|
|
2961
|
+
typography: "display",
|
|
2962
|
+
spacing: "xl"
|
|
2963
|
+
}
|
|
2964
|
+
});
|
|
2965
|
+
var heroBlockDefinition = {
|
|
2966
|
+
manifest: heroManifest
|
|
2967
|
+
};
|
|
2968
|
+
|
|
2969
|
+
// ../blocks/src/system/blocks/body-text.ts
|
|
2970
|
+
var bodyCopyComposition = composeFragments([{ fragment: bodyCopyFragment }]);
|
|
2971
|
+
var bodyTextManifest = createBlockManifest({
|
|
2972
|
+
id: "block.bodyText",
|
|
2973
|
+
title: "Body Text",
|
|
2974
|
+
titleSource: "heading",
|
|
2975
|
+
category: "content",
|
|
2976
|
+
component: "body.text.basic",
|
|
2977
|
+
fragments: [
|
|
2978
|
+
{ fragment: bodyCopyFragment, fieldPriority: 0 }
|
|
2979
|
+
],
|
|
2980
|
+
layout: styledSection({
|
|
2981
|
+
children: bodyCopyComposition.layout,
|
|
2982
|
+
spacing: "medium"
|
|
2983
|
+
}),
|
|
2984
|
+
description: "Simple text block with optional heading and alignment controls.",
|
|
2985
|
+
tags: ["text", "paragraph", "content", "copy", "article", "writing", "rich-text"],
|
|
2986
|
+
icon: "Type",
|
|
2987
|
+
styleTokens: {
|
|
2988
|
+
spacing: "md"
|
|
2989
|
+
}
|
|
2990
|
+
});
|
|
2991
|
+
var bodyTextBlockDefinition = {
|
|
2992
|
+
manifest: bodyTextManifest
|
|
2993
|
+
};
|
|
2994
|
+
var composition = composeFragments([{ fragment: blogFeaturedPostFragment }]);
|
|
2995
|
+
var fields = fieldSchema.array().parse(composition.fields);
|
|
2996
|
+
var blogPostLayout = section(
|
|
2997
|
+
{ background: "background/base", className: "px-6 py-12" },
|
|
2998
|
+
[...composition.layout]
|
|
2999
|
+
);
|
|
3000
|
+
var blogPostManifest = {
|
|
3001
|
+
name: "block.blogPost",
|
|
3002
|
+
version: "0.1.0",
|
|
3003
|
+
title: "Blog post",
|
|
3004
|
+
description: "Highlights a single blog post with title, image, and excerpt.",
|
|
3005
|
+
component: "blog.post.highlight",
|
|
3006
|
+
fields,
|
|
3007
|
+
slots: [],
|
|
3008
|
+
styleTokens: {
|
|
3009
|
+
background: "surface",
|
|
3010
|
+
typography: "body",
|
|
3011
|
+
spacing: "md"
|
|
3012
|
+
},
|
|
3013
|
+
behaviours: {
|
|
3014
|
+
supportsThemeSwitching: true,
|
|
3015
|
+
inlineEditing: false,
|
|
3016
|
+
animation: false,
|
|
3017
|
+
paletteHidden: false
|
|
3018
|
+
},
|
|
3019
|
+
category: "blog",
|
|
3020
|
+
contentTypes: ["post"],
|
|
3021
|
+
tags: ["blog", "post", "featured", "highlight", "article", "single"],
|
|
3022
|
+
icon: "FileText",
|
|
3023
|
+
layout: blogPostLayout
|
|
3024
|
+
};
|
|
3025
|
+
var blogPostDataSchema = zod.z.object({
|
|
3026
|
+
id: zod.z.string(),
|
|
3027
|
+
title: zod.z.string(),
|
|
3028
|
+
slug: zod.z.string(),
|
|
3029
|
+
path: zod.z.string(),
|
|
3030
|
+
excerpt: zod.z.string().nullable().optional(),
|
|
3031
|
+
image: zod.z.object({
|
|
3032
|
+
url: zod.z.string().optional().nullable(),
|
|
3033
|
+
alt: zod.z.string().optional().nullable()
|
|
3034
|
+
}).nullable().optional(),
|
|
3035
|
+
publishedAt: zod.z.string().nullable().optional()
|
|
3036
|
+
});
|
|
3037
|
+
var blogPostBlockDefinition = {
|
|
3038
|
+
manifest: blogPostManifest,
|
|
3039
|
+
dataSchemas: { post: blogPostDataSchema.optional() },
|
|
3040
|
+
dataLoaders: buildFragmentDataLoaders(composition)
|
|
3041
|
+
};
|
|
3042
|
+
|
|
3043
|
+
// ../blocks/src/system/blocks/blog-placeholder.ts
|
|
3044
|
+
var grid2 = (props2, children, ...mods) => el("grid", props2 ?? void 0, children ?? void 0, ...mods);
|
|
3045
|
+
var blogPlaceholderLayout = section(
|
|
3046
|
+
{ background: "surface", className: "px-6 py-16 sm:py-20 md:py-24" },
|
|
3047
|
+
// Semantic: comfortable + extra md padding
|
|
3048
|
+
[
|
|
3049
|
+
stack(
|
|
3050
|
+
{ gap: "sm", align: "center", className: "mx-auto max-w-3xl text-center" },
|
|
3051
|
+
[
|
|
3052
|
+
text(
|
|
3053
|
+
{ as: "p", className: "text-sm font-semibold uppercase tracking-wide", style: textColorStyle("mutedText") },
|
|
3054
|
+
bind("content.eyebrow")
|
|
3055
|
+
),
|
|
3056
|
+
text(
|
|
3057
|
+
{ as: "h2", className: "text-3xl font-semibold tracking-tight sm:text-4xl", style: textColorStyle("text") },
|
|
3058
|
+
bind("content.heading")
|
|
3059
|
+
),
|
|
3060
|
+
text(
|
|
3061
|
+
{ className: "text-base leading-relaxed", style: textColorStyle("mutedText") },
|
|
3062
|
+
bind("content.description")
|
|
3063
|
+
)
|
|
3064
|
+
]
|
|
3065
|
+
),
|
|
3066
|
+
grid2(
|
|
3067
|
+
{ className: "mt-10 gap-6 sm:grid-cols-2 lg:grid-cols-3" },
|
|
3068
|
+
[
|
|
3069
|
+
stack(
|
|
3070
|
+
{
|
|
3071
|
+
gap: "sm",
|
|
3072
|
+
className: "rounded-xl border p-6 text-left shadow-sm",
|
|
3073
|
+
style: mergeStyles(borderColorStyle("border/60"), backgroundColorStyle("background"))
|
|
3074
|
+
},
|
|
3075
|
+
[
|
|
3076
|
+
text(
|
|
3077
|
+
{ as: "h3", className: "text-lg font-semibold", style: textColorStyle("text") },
|
|
3078
|
+
bind("item.title")
|
|
3079
|
+
),
|
|
3080
|
+
text(
|
|
3081
|
+
{ className: "text-sm leading-relaxed", style: textColorStyle("mutedText") },
|
|
3082
|
+
bind("item.summary")
|
|
3083
|
+
)
|
|
3084
|
+
],
|
|
3085
|
+
repeat("content.cards", "item", { limit: 6 })
|
|
3086
|
+
)
|
|
3087
|
+
]
|
|
3088
|
+
),
|
|
3089
|
+
stack(
|
|
3090
|
+
{ gap: "xs", align: "center", className: "mt-10 text-center" },
|
|
3091
|
+
[
|
|
3092
|
+
text({ className: "text-sm", style: textColorStyle("mutedText") }, bind("content.note"))
|
|
3093
|
+
]
|
|
3094
|
+
)
|
|
3095
|
+
]
|
|
3096
|
+
);
|
|
3097
|
+
var blogPlaceholderManifest = createBlockManifest({
|
|
3098
|
+
id: "block.blogPlaceholder",
|
|
3099
|
+
title: "Blog placeholder",
|
|
3100
|
+
category: "blog",
|
|
3101
|
+
component: "blog.placeholder",
|
|
3102
|
+
// Skip section styles - this block has custom layout with fixed styling
|
|
3103
|
+
skipSectionStyles: true,
|
|
3104
|
+
// Custom fields for blog placeholder content
|
|
3105
|
+
additionalFields: [
|
|
3106
|
+
fieldSchema.parse({
|
|
3107
|
+
id: "eyebrow",
|
|
3108
|
+
type: "text",
|
|
3109
|
+
label: "Eyebrow",
|
|
3110
|
+
description: "Short label displayed above the heading."
|
|
3111
|
+
}),
|
|
3112
|
+
fieldSchema.parse({
|
|
3113
|
+
id: "heading",
|
|
3114
|
+
type: "text",
|
|
3115
|
+
label: "Heading",
|
|
3116
|
+
required: true
|
|
3117
|
+
}),
|
|
3118
|
+
fieldSchema.parse({
|
|
3119
|
+
id: "description",
|
|
3120
|
+
type: "text",
|
|
3121
|
+
label: "Description",
|
|
3122
|
+
multiline: true
|
|
3123
|
+
}),
|
|
3124
|
+
fieldSchema.parse({
|
|
3125
|
+
id: "cards",
|
|
3126
|
+
type: "repeater",
|
|
3127
|
+
label: "Placeholder cards",
|
|
3128
|
+
itemLabel: "Placeholder",
|
|
3129
|
+
schema: {
|
|
3130
|
+
fields: [
|
|
3131
|
+
{ id: "title", type: "text", label: "Title", required: true },
|
|
3132
|
+
{ id: "summary", type: "text", label: "Summary", multiline: true, required: true }
|
|
3133
|
+
]
|
|
3134
|
+
}
|
|
3135
|
+
}),
|
|
3136
|
+
fieldSchema.parse({
|
|
3137
|
+
id: "note",
|
|
3138
|
+
type: "text",
|
|
3139
|
+
label: "Footer note",
|
|
3140
|
+
description: "Optional helper text displayed below the cards."
|
|
3141
|
+
})
|
|
3142
|
+
],
|
|
3143
|
+
layout: blogPlaceholderLayout,
|
|
3144
|
+
description: "Starter layout shown until real blog content replaces it.",
|
|
3145
|
+
styleTokens: {
|
|
3146
|
+
background: "surface",
|
|
3147
|
+
spacing: "lg"
|
|
3148
|
+
},
|
|
3149
|
+
paletteHidden: true
|
|
3150
|
+
});
|
|
3151
|
+
var blogPlaceholderBlockDefinition = {
|
|
3152
|
+
manifest: blogPlaceholderManifest
|
|
3153
|
+
};
|
|
3154
|
+
var gridComposition = composeFragments([{ fragment: blogListGridFragment }]);
|
|
3155
|
+
var stackComposition = composeFragments([{ fragment: blogListStackFragment }]);
|
|
3156
|
+
var fragmentFields = [...gridComposition.fields, ...stackComposition.fields];
|
|
3157
|
+
var uniqueFields = fragmentFields.filter(
|
|
3158
|
+
(field, index, self) => index === self.findIndex((f) => f.id === field.id)
|
|
3159
|
+
);
|
|
3160
|
+
var blogListingLayout = section(
|
|
3161
|
+
{ background: "background/base", className: "px-6 py-12 sm:py-16 md:py-20" },
|
|
3162
|
+
// Semantic: medium + extra md padding
|
|
3163
|
+
[
|
|
3164
|
+
// Grid layout (conditionally shown)
|
|
3165
|
+
...gridComposition.layout.map((node) => ({
|
|
3166
|
+
...node,
|
|
3167
|
+
$when: {
|
|
3168
|
+
when: { from: "content.layout" },
|
|
3169
|
+
equals: "grid"
|
|
3170
|
+
}
|
|
3171
|
+
})),
|
|
3172
|
+
// Stack layout (conditionally shown)
|
|
3173
|
+
...stackComposition.layout.map((node) => ({
|
|
3174
|
+
...node,
|
|
3175
|
+
$when: {
|
|
3176
|
+
when: { from: "content.layout" },
|
|
3177
|
+
equals: "stack"
|
|
3178
|
+
}
|
|
3179
|
+
})),
|
|
3180
|
+
// Empty state (shown when no posts)
|
|
3181
|
+
stack(
|
|
3182
|
+
{
|
|
3183
|
+
gap: "sm",
|
|
3184
|
+
className: "mx-auto max-w-3xl rounded-xl border border-dashed p-12 text-center",
|
|
3185
|
+
style: mergeStyles(
|
|
3186
|
+
borderColorStyle("neutral-300"),
|
|
3187
|
+
backgroundColorStyle("neutral-50")
|
|
3188
|
+
)
|
|
3189
|
+
},
|
|
3190
|
+
[
|
|
3191
|
+
text(
|
|
3192
|
+
{ as: "p", className: "text-base", style: textColorStyle("neutral-500") },
|
|
3193
|
+
bind("content.emptyMessage", { fallback: "No posts published yet." })
|
|
3194
|
+
)
|
|
3195
|
+
],
|
|
3196
|
+
when("data.posts", { not: true })
|
|
3197
|
+
)
|
|
3198
|
+
]
|
|
3199
|
+
);
|
|
3200
|
+
var blogListingManifest = createBlockManifest({
|
|
3201
|
+
id: "block.blogListing",
|
|
3202
|
+
title: "Blog listing",
|
|
3203
|
+
category: "blog",
|
|
3204
|
+
component: "blog.listing",
|
|
3205
|
+
// Skip section styles - this block has layout-specific fields instead
|
|
3206
|
+
skipSectionStyles: true,
|
|
3207
|
+
// Custom fields for blog listing configuration
|
|
3208
|
+
additionalFields: [
|
|
3209
|
+
fieldSchema.parse({
|
|
3210
|
+
id: "layout",
|
|
3211
|
+
type: "select",
|
|
3212
|
+
label: "Layout",
|
|
3213
|
+
description: "Choose how blog posts are displayed.",
|
|
3214
|
+
defaultValue: "grid",
|
|
3215
|
+
options: [
|
|
3216
|
+
{ value: "grid", label: "Grid (cards)" },
|
|
3217
|
+
{ value: "stack", label: "Stack (vertical list)" }
|
|
3218
|
+
]
|
|
3219
|
+
}),
|
|
3220
|
+
// Grid-specific column field (only visible when layout is grid)
|
|
3221
|
+
...gridComposition.fields.filter((f) => f.id === "columns").map((f) => ({
|
|
3222
|
+
...f,
|
|
3223
|
+
ui: {
|
|
3224
|
+
...f.ui,
|
|
3225
|
+
visibleWhen: { field: "layout", equals: "grid" }
|
|
3226
|
+
}
|
|
3227
|
+
})),
|
|
3228
|
+
fieldSchema.parse({
|
|
3229
|
+
id: "postsPerPage",
|
|
3230
|
+
type: "select",
|
|
3231
|
+
label: "Posts to display",
|
|
3232
|
+
description: "Number of posts to show on this page.",
|
|
3233
|
+
defaultValue: "12",
|
|
3234
|
+
options: [
|
|
3235
|
+
{ value: "6", label: "6 posts" },
|
|
3236
|
+
{ value: "12", label: "12 posts" },
|
|
3237
|
+
{ value: "24", label: "24 posts" },
|
|
3238
|
+
{ value: "48", label: "48 posts" }
|
|
3239
|
+
]
|
|
3240
|
+
}),
|
|
3241
|
+
// Shared toggle fields from fragments (now unscoped)
|
|
3242
|
+
...uniqueFields.filter((f) => ["showImages", "showExcerpts", "showDates"].includes(f.id)),
|
|
3243
|
+
fieldSchema.parse({
|
|
3244
|
+
id: "readMoreText",
|
|
3245
|
+
type: "text",
|
|
3246
|
+
label: "Read more text",
|
|
3247
|
+
description: "Text for the read more button/link.",
|
|
3248
|
+
defaultValue: "Read more \u2192",
|
|
3249
|
+
maxLength: 40
|
|
3250
|
+
}),
|
|
3251
|
+
fieldSchema.parse({
|
|
3252
|
+
id: "readMoreVariant",
|
|
3253
|
+
type: "select",
|
|
3254
|
+
label: "Read more style",
|
|
3255
|
+
description: "Visual style for the read more button.",
|
|
3256
|
+
defaultValue: "link",
|
|
3257
|
+
options: [
|
|
3258
|
+
{ value: "link", label: "Link (minimal)" },
|
|
3259
|
+
{ value: "primary", label: "Primary button" },
|
|
3260
|
+
{ value: "secondary", label: "Secondary button" },
|
|
3261
|
+
{ value: "outline", label: "Outline button" }
|
|
3262
|
+
]
|
|
3263
|
+
}),
|
|
3264
|
+
fieldSchema.parse({
|
|
3265
|
+
id: "emptyMessage",
|
|
3266
|
+
type: "text",
|
|
3267
|
+
label: "Empty state message",
|
|
3268
|
+
description: "Message shown when no posts are available.",
|
|
3269
|
+
defaultValue: "No posts published yet.",
|
|
3270
|
+
maxLength: 200
|
|
3271
|
+
})
|
|
3272
|
+
],
|
|
3273
|
+
layout: blogListingLayout,
|
|
3274
|
+
description: "Display a collection of blog posts in grid or stack layout.",
|
|
3275
|
+
contentTypes: ["post"],
|
|
3276
|
+
tags: ["blog", "posts", "articles", "news", "archive", "listing", "feed", "index"],
|
|
3277
|
+
icon: "Newspaper",
|
|
3278
|
+
styleTokens: {
|
|
3279
|
+
background: "surface",
|
|
3280
|
+
spacing: "lg"
|
|
3281
|
+
}
|
|
3282
|
+
});
|
|
3283
|
+
var blogPostListEntrySchema = zod.z.object({
|
|
3284
|
+
id: zod.z.string(),
|
|
3285
|
+
slug: zod.z.string(),
|
|
3286
|
+
path: zod.z.string(),
|
|
3287
|
+
title: zod.z.string(),
|
|
3288
|
+
excerpt: zod.z.string().nullable().optional(),
|
|
3289
|
+
publishedAt: zod.z.string().nullable().optional(),
|
|
3290
|
+
updatedAt: zod.z.string(),
|
|
3291
|
+
status: zod.z.string(),
|
|
3292
|
+
image: zod.z.object({
|
|
3293
|
+
url: zod.z.string(),
|
|
3294
|
+
alt: zod.z.string().optional()
|
|
3295
|
+
}).nullable().optional()
|
|
3296
|
+
});
|
|
3297
|
+
var blogListingBlockDefinition = {
|
|
3298
|
+
manifest: blogListingManifest,
|
|
3299
|
+
dataSchemas: {
|
|
3300
|
+
posts: zod.z.array(blogPostListEntrySchema).optional()
|
|
3301
|
+
},
|
|
3302
|
+
dataLoaders: buildFragmentDataLoaders(gridComposition)
|
|
3303
|
+
// Use grid composition for loader config (same for both)
|
|
3304
|
+
};
|
|
3305
|
+
|
|
3306
|
+
// ../blocks/src/system/blocks/cta-full.ts
|
|
3307
|
+
var ctaComposition = composeFragments([
|
|
3308
|
+
{ fragment: ctaCopyFragment },
|
|
3309
|
+
{ fragment: ctaRowFragment }
|
|
3310
|
+
]);
|
|
3311
|
+
var ctaFullManifest = createBlockManifest({
|
|
3312
|
+
id: "block.ctaFull",
|
|
3313
|
+
title: "Full-width CTA",
|
|
3314
|
+
category: "marketing",
|
|
3315
|
+
fragments: [
|
|
3316
|
+
{ fragment: ctaCopyFragment, fieldPriority: 0 },
|
|
3317
|
+
{ fragment: ctaRowFragment, fieldPriority: 2 }
|
|
3318
|
+
],
|
|
3319
|
+
layout: styledSection({
|
|
3320
|
+
children: sectionContainer(ctaComposition.layout, {
|
|
3321
|
+
gap: "md",
|
|
3322
|
+
className: "relative text-center"
|
|
3323
|
+
})
|
|
3324
|
+
}),
|
|
3325
|
+
description: "Centered call to action with optional background image.",
|
|
3326
|
+
tags: ["cta", "call-to-action", "button", "conversion", "action", "sign-up", "get-started"],
|
|
3327
|
+
icon: "Target"
|
|
3328
|
+
});
|
|
3329
|
+
var ctaFullBlockDefinition = {
|
|
3330
|
+
manifest: ctaFullManifest
|
|
3331
|
+
};
|
|
3332
|
+
var formComposition = composeFragments([
|
|
3333
|
+
{ fragment: formCopyFragment },
|
|
3334
|
+
{ fragment: formEmbedFragment }
|
|
3335
|
+
]);
|
|
3336
|
+
var formLayout = section(
|
|
3337
|
+
{ background: "surface", className: "px-6 py-12" },
|
|
3338
|
+
[sectionContainer(formComposition.layout, { gap: "lg" })]
|
|
3339
|
+
);
|
|
3340
|
+
var fields2 = fieldSchema.array().parse(formComposition.fields);
|
|
3341
|
+
var dataLoaders = buildFragmentDataLoaders(formComposition);
|
|
3342
|
+
var formManifest = {
|
|
3343
|
+
name: "block.form",
|
|
3344
|
+
version: "0.1.0",
|
|
3345
|
+
title: "Form",
|
|
3346
|
+
titleSource: "title",
|
|
3347
|
+
description: "Renders a saved form definition with server-side submit.",
|
|
3348
|
+
component: "form.block",
|
|
3349
|
+
fields: fields2,
|
|
3350
|
+
slots: [],
|
|
3351
|
+
styleTokens: { background: "surface", typography: "body", spacing: "md" },
|
|
3352
|
+
behaviours: { supportsThemeSwitching: true, inlineEditing: false, animation: false, paletteHidden: false },
|
|
3353
|
+
category: "interactive",
|
|
3354
|
+
tags: ["form", "contact", "input", "submit", "fields", "signup", "lead-capture"],
|
|
3355
|
+
icon: "FormInput",
|
|
3356
|
+
layout: formLayout
|
|
3357
|
+
};
|
|
3358
|
+
var formDataSchema = zod.z.object({
|
|
3359
|
+
id: zod.z.string(),
|
|
3360
|
+
siteId: zod.z.string(),
|
|
3361
|
+
userId: zod.z.string(),
|
|
3362
|
+
name: zod.z.string(),
|
|
3363
|
+
slug: zod.z.string(),
|
|
3364
|
+
schemaJson: zod.z.any(),
|
|
3365
|
+
settingsJson: zod.z.any().optional(),
|
|
3366
|
+
createdAt: zod.z.string(),
|
|
3367
|
+
updatedAt: zod.z.string()
|
|
3368
|
+
});
|
|
3369
|
+
var formBlockDefinition = {
|
|
3370
|
+
manifest: formManifest,
|
|
3371
|
+
dataSchemas: { form: formDataSchema.optional() },
|
|
3372
|
+
dataLoaders
|
|
3373
|
+
};
|
|
3374
|
+
|
|
3375
|
+
// ../blocks/src/system/blocks/faq.ts
|
|
3376
|
+
var faqComposition = composeFragments([
|
|
3377
|
+
{ fragment: faqHeadingFragment, fieldPriority: 0 },
|
|
3378
|
+
{ fragment: faqAccordionFragment, fieldPriority: 1 }
|
|
3379
|
+
]);
|
|
3380
|
+
var faqManifest = createBlockManifest({
|
|
3381
|
+
id: "block.faq",
|
|
3382
|
+
title: "FAQ",
|
|
3383
|
+
titleSource: "title",
|
|
3384
|
+
category: "content",
|
|
3385
|
+
fragments: [
|
|
3386
|
+
{ fragment: faqHeadingFragment, fieldPriority: 0 },
|
|
3387
|
+
{ fragment: faqAccordionFragment, fieldPriority: 1 }
|
|
3388
|
+
],
|
|
3389
|
+
layout: styledSection({
|
|
3390
|
+
children: sectionContainer(faqComposition.layout, {
|
|
3391
|
+
gap: "xl",
|
|
3392
|
+
className: "w-full"
|
|
3393
|
+
})
|
|
3394
|
+
}),
|
|
3395
|
+
description: "Accordion of frequently asked questions and answers.",
|
|
3396
|
+
tags: ["faq", "questions", "answers", "help", "support", "accordion", "q&a"],
|
|
3397
|
+
icon: "HelpCircle"
|
|
3398
|
+
});
|
|
3399
|
+
var faqBlockDefinition = {
|
|
3400
|
+
manifest: faqManifest
|
|
3401
|
+
};
|
|
3402
|
+
|
|
3403
|
+
// ../blocks/src/system/transforms/typed.ts
|
|
3404
|
+
function tx(id, options) {
|
|
3405
|
+
return { id, options };
|
|
3406
|
+
}
|
|
3407
|
+
function pipe(...steps) {
|
|
3408
|
+
return steps;
|
|
3409
|
+
}
|
|
3410
|
+
function bindProp(from, opts) {
|
|
3411
|
+
return {
|
|
3412
|
+
$bind: {
|
|
3413
|
+
from,
|
|
3414
|
+
...opts?.transforms ? { transforms: [...opts.transforms] } : {},
|
|
3415
|
+
...opts?.fallback !== void 0 ? { fallback: opts.fallback } : {},
|
|
3416
|
+
...opts?.pick ? { pick: opts.pick } : {}
|
|
3417
|
+
}
|
|
3418
|
+
};
|
|
3419
|
+
}
|
|
3420
|
+
|
|
3421
|
+
// ../blocks/src/system/blocks/site-header.ts
|
|
3422
|
+
var logoRow = link(
|
|
3423
|
+
{ href: "/", className: "header-logo flex min-w-0 items-center gap-3 no-underline transition-opacity hover:opacity-80" },
|
|
3424
|
+
[
|
|
3425
|
+
media({ className: "h-10 w-auto transition-all duration-300 [.header-scrolled_&]:h-8" }, when("content.logo"), bind("content.logo")),
|
|
3426
|
+
text({ as: "span", className: "header-logo-text truncate text-lg font-semibold" }, bind("site.title", { fallback: "Your Site" }))
|
|
3427
|
+
]
|
|
3428
|
+
);
|
|
3429
|
+
var centeredLogoRow = link(
|
|
3430
|
+
{ href: "/", className: "header-logo flex items-center justify-center gap-3 text-center no-underline transition-opacity hover:opacity-80" },
|
|
3431
|
+
[
|
|
3432
|
+
media({ className: "h-12 w-auto transition-all duration-300 [.header-scrolled_&]:h-10" }, when("content.logo"), bind("content.logo")),
|
|
3433
|
+
text({ as: "span", className: "header-logo-text text-xl font-semibold" }, bind("site.title", { fallback: "Your Site" }))
|
|
3434
|
+
]
|
|
3435
|
+
);
|
|
3436
|
+
var createNavRow = (className, align = "end") => navRow({
|
|
3437
|
+
className: `${className} header-nav-row`,
|
|
3438
|
+
align,
|
|
3439
|
+
linkClassName: "header-nav-link inline-flex items-center px-4 py-2 text-sm font-medium transition-theme-standard"
|
|
3440
|
+
});
|
|
3441
|
+
var headerCta = ctaButton({
|
|
3442
|
+
basePath: "menu.ctaItem",
|
|
3443
|
+
whenPath: "menu.ctaItem.label",
|
|
3444
|
+
variantPath: "menu.ctaItem.variant",
|
|
3445
|
+
linkPath: "menu.ctaItem.link",
|
|
3446
|
+
className: "header-cta btn-sm hidden md:inline-flex ml-6"
|
|
3447
|
+
});
|
|
3448
|
+
var classicLayout2 = inline(
|
|
3449
|
+
{
|
|
3450
|
+
className: bindProp("$root.theme.header.maxWidth", {
|
|
3451
|
+
transforms: pipe(tx("layout.maxWidthClass", { base: "flex w-full items-center gap-6 py-4" })),
|
|
3452
|
+
fallback: "container mx-auto flex w-full items-center gap-6 px-6 py-4"
|
|
3453
|
+
}),
|
|
3454
|
+
align: "center"
|
|
3455
|
+
},
|
|
3456
|
+
[
|
|
3457
|
+
logoRow,
|
|
3458
|
+
createNavRow("ml-auto hidden md:flex gap-6"),
|
|
3459
|
+
headerCta
|
|
3460
|
+
],
|
|
3461
|
+
when("$root.theme.header.variant", { equals: "classic" })
|
|
3462
|
+
);
|
|
3463
|
+
var centeredLayout = stack(
|
|
3464
|
+
{
|
|
3465
|
+
gap: "md",
|
|
3466
|
+
align: "center",
|
|
3467
|
+
className: bindProp("$root.theme.header.maxWidth", {
|
|
3468
|
+
transforms: pipe(tx("layout.maxWidthClass", { base: "flex w-full flex-col items-center gap-5 py-6 text-center" })),
|
|
3469
|
+
fallback: "container mx-auto flex w-full flex-col items-center gap-5 px-6 py-6 text-center"
|
|
3470
|
+
})
|
|
3471
|
+
},
|
|
3472
|
+
[
|
|
3473
|
+
centeredLogoRow,
|
|
3474
|
+
createNavRow("flex flex-wrap justify-center gap-x-6 gap-y-3", "center")
|
|
3475
|
+
],
|
|
3476
|
+
when("$root.theme.header.variant", { equals: "centered" })
|
|
3477
|
+
);
|
|
3478
|
+
var transparentLayout = inline(
|
|
3479
|
+
{
|
|
3480
|
+
className: bindProp("$root.theme.header.maxWidth", {
|
|
3481
|
+
transforms: pipe(tx("layout.maxWidthClass", { base: "flex w-full items-center gap-6 py-4" })),
|
|
3482
|
+
fallback: "container mx-auto flex w-full items-center gap-6 px-6 py-4"
|
|
3483
|
+
}),
|
|
3484
|
+
align: "center"
|
|
3485
|
+
},
|
|
3486
|
+
[
|
|
3487
|
+
logoRow,
|
|
3488
|
+
createNavRow("ml-auto hidden md:flex gap-6"),
|
|
3489
|
+
headerCta
|
|
3490
|
+
],
|
|
3491
|
+
when("$root.theme.header.variant", { equals: "transparent" })
|
|
3492
|
+
);
|
|
3493
|
+
var floatingLayout = inline(
|
|
3494
|
+
{
|
|
3495
|
+
className: bindProp("$root.theme.header.maxWidth", {
|
|
3496
|
+
transforms: pipe(tx("layout.maxWidthClass", { base: "header-floating-container absolute left-1/2 top-4 flex w-[calc(100%-2rem)] max-w-7xl -translate-x-1/2 items-center gap-6 px-6 py-3" })),
|
|
3497
|
+
fallback: "header-floating-container absolute left-1/2 top-4 flex w-[calc(100%-2rem)] max-w-7xl -translate-x-1/2 items-center gap-6 px-6 py-3"
|
|
3498
|
+
}),
|
|
3499
|
+
align: "center"
|
|
3500
|
+
},
|
|
3501
|
+
[
|
|
3502
|
+
logoRow,
|
|
3503
|
+
createNavRow("ml-auto hidden md:flex gap-6"),
|
|
3504
|
+
headerCta
|
|
3505
|
+
],
|
|
3506
|
+
when("$root.theme.header.variant", { equals: "floating" })
|
|
3507
|
+
);
|
|
3508
|
+
var editorialLayout = stack(
|
|
3509
|
+
{
|
|
3510
|
+
gap: "md",
|
|
3511
|
+
align: "center",
|
|
3512
|
+
className: bindProp("$root.theme.header.maxWidth", {
|
|
3513
|
+
transforms: pipe(tx("layout.maxWidthClass", { base: "flex w-full flex-col items-center gap-6 py-6 text-center" })),
|
|
3514
|
+
fallback: "container mx-auto flex w-full flex-col items-center gap-6 px-6 py-6 text-center"
|
|
3515
|
+
})
|
|
3516
|
+
},
|
|
3517
|
+
[
|
|
3518
|
+
centeredLogoRow,
|
|
3519
|
+
createNavRow("flex flex-wrap justify-center gap-x-8 gap-y-3", "center")
|
|
3520
|
+
],
|
|
3521
|
+
when("$root.theme.header.variant", { equals: "editorial" })
|
|
3522
|
+
);
|
|
3523
|
+
var headerLayout = headerSection(
|
|
3524
|
+
{
|
|
3525
|
+
background: "background/base",
|
|
3526
|
+
className: bindProp("$root.theme.header", {
|
|
3527
|
+
transforms: pipe(tx("layout.headerRootClass")),
|
|
3528
|
+
fallback: "header-root z-40 w-full border-b transition-theme backdrop-blur"
|
|
3529
|
+
}),
|
|
3530
|
+
style: bindProp("$root.theme.header", {
|
|
3531
|
+
transforms: pipe(tx("layout.headerRootStyle")),
|
|
3532
|
+
fallback: mergeStyles(
|
|
3533
|
+
backgroundColorStyle("surface"),
|
|
3534
|
+
textColorStyle("text"),
|
|
3535
|
+
borderColorStyle("border")
|
|
3536
|
+
)
|
|
3537
|
+
})
|
|
3538
|
+
},
|
|
3539
|
+
[
|
|
3540
|
+
classicLayout2,
|
|
3541
|
+
centeredLayout,
|
|
3542
|
+
transparentLayout,
|
|
3543
|
+
floatingLayout,
|
|
3544
|
+
editorialLayout
|
|
3545
|
+
],
|
|
3546
|
+
props({
|
|
3547
|
+
"data-site-header": "true"
|
|
3548
|
+
})
|
|
3549
|
+
);
|
|
3550
|
+
var siteHeaderManifest = createBlockManifest({
|
|
3551
|
+
id: "block.siteHeader",
|
|
3552
|
+
title: "Site Header",
|
|
3553
|
+
category: "layout",
|
|
3554
|
+
component: "site.header.default",
|
|
3555
|
+
// Skip section styles - this block uses theme-based styling
|
|
3556
|
+
skipSectionStyles: true,
|
|
3557
|
+
layout: headerLayout,
|
|
3558
|
+
description: "Site-wide header with logo, navigation, and optional CTA.",
|
|
3559
|
+
styleTokens: {
|
|
3560
|
+
spacing: "sm"
|
|
3561
|
+
},
|
|
3562
|
+
paletteHidden: true
|
|
3563
|
+
});
|
|
3564
|
+
var siteHeaderBlockDefinition = {
|
|
3565
|
+
manifest: siteHeaderManifest
|
|
3566
|
+
};
|
|
3567
|
+
|
|
3568
|
+
// ../blocks/src/system/blocks/site-footer.ts
|
|
3569
|
+
composeFragments([
|
|
3570
|
+
{ fragment: footerLinkGroupsFragment, fieldPriority: 0 },
|
|
3571
|
+
{ fragment: footerBottomTextFragment, fieldPriority: 1 }
|
|
3572
|
+
]);
|
|
3573
|
+
var linkGroupsLayout = () => materializeFragment({ fragment: footerLinkGroupsFragment }).layout;
|
|
3574
|
+
var bottomTextLayout = () => materializeFragment({ fragment: footerBottomTextFragment }).layout;
|
|
3575
|
+
var simpleFooterLayout = stack(
|
|
3576
|
+
{
|
|
3577
|
+
gap: "md",
|
|
3578
|
+
align: "center",
|
|
3579
|
+
className: {
|
|
3580
|
+
$bind: {
|
|
3581
|
+
from: "$root.theme.footer.maxWidth",
|
|
3582
|
+
transforms: [
|
|
3583
|
+
{
|
|
3584
|
+
id: "layout.maxWidthClass",
|
|
3585
|
+
options: { base: "flex w-full flex-col items-center gap-4 py-10 text-center" }
|
|
3586
|
+
}
|
|
3587
|
+
],
|
|
3588
|
+
fallback: "container mx-auto flex w-full flex-col items-center gap-4 px-6 py-10 text-center"
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
},
|
|
3592
|
+
[
|
|
3593
|
+
navRow({ align: "center", className: "flex flex-wrap justify-center gap-x-6 gap-y-3" }),
|
|
3594
|
+
...bottomTextLayout()
|
|
3595
|
+
],
|
|
3596
|
+
when("$root.theme.footer.variant", { equals: "simple" })
|
|
3597
|
+
);
|
|
3598
|
+
var columnsFooterLayout = stack(
|
|
3599
|
+
{
|
|
3600
|
+
gap: "xl",
|
|
3601
|
+
align: "start",
|
|
3602
|
+
className: {
|
|
3603
|
+
$bind: {
|
|
3604
|
+
from: "$root.theme.footer.maxWidth",
|
|
3605
|
+
transforms: [
|
|
3606
|
+
{
|
|
3607
|
+
id: "layout.maxWidthClass",
|
|
3608
|
+
options: { base: "flex w-full flex-col gap-10 py-12" }
|
|
3609
|
+
}
|
|
3610
|
+
],
|
|
3611
|
+
fallback: "container mx-auto flex w-full flex-col gap-10 px-6 py-12"
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
},
|
|
3615
|
+
[
|
|
3616
|
+
...linkGroupsLayout(),
|
|
3617
|
+
inline(
|
|
3618
|
+
{ className: "flex w-full flex-wrap items-center justify-between gap-4" },
|
|
3619
|
+
[
|
|
3620
|
+
text({ as: "span", className: "text-sm font-semibold", style: textColorStyle("text") }, bind("site.title", { fallback: "Your Site" })),
|
|
3621
|
+
navRow({ className: "flex flex-wrap justify-end gap-x-6 gap-y-3", align: "end" })
|
|
3622
|
+
]
|
|
3623
|
+
),
|
|
3624
|
+
...bottomTextLayout()
|
|
3625
|
+
],
|
|
3626
|
+
when("$root.theme.footer.variant", { equals: "columns" })
|
|
3627
|
+
);
|
|
3628
|
+
var footerLayout = section(
|
|
3629
|
+
{
|
|
3630
|
+
background: "background/base",
|
|
3631
|
+
className: {
|
|
3632
|
+
$bind: {
|
|
3633
|
+
from: "$root.theme.footer",
|
|
3634
|
+
transforms: [
|
|
3635
|
+
{
|
|
3636
|
+
id: "layout.footerRootClass"
|
|
3637
|
+
}
|
|
3638
|
+
],
|
|
3639
|
+
fallback: "w-full border-t transition-theme"
|
|
3640
|
+
}
|
|
3641
|
+
},
|
|
3642
|
+
style: {
|
|
3643
|
+
$bind: {
|
|
3644
|
+
from: "$root.theme.footer",
|
|
3645
|
+
transforms: [
|
|
3646
|
+
{
|
|
3647
|
+
id: "layout.footerRootStyle"
|
|
3648
|
+
}
|
|
3649
|
+
],
|
|
3650
|
+
fallback: mergeStyles(
|
|
3651
|
+
backgroundColorStyle("surface"),
|
|
3652
|
+
textColorStyle("text"),
|
|
3653
|
+
borderColorStyle("border")
|
|
3654
|
+
)
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
},
|
|
3658
|
+
[
|
|
3659
|
+
simpleFooterLayout,
|
|
3660
|
+
columnsFooterLayout
|
|
3661
|
+
]
|
|
3662
|
+
);
|
|
3663
|
+
var siteFooterManifest = createBlockManifest({
|
|
3664
|
+
id: "block.siteFooter",
|
|
3665
|
+
title: "Site Footer",
|
|
3666
|
+
category: "layout",
|
|
3667
|
+
component: "site.footer.default",
|
|
3668
|
+
// Use fragments for link groups and bottom text
|
|
3669
|
+
fragments: [
|
|
3670
|
+
{ fragment: footerLinkGroupsFragment, fieldPriority: 0 },
|
|
3671
|
+
{ fragment: footerBottomTextFragment, fieldPriority: 1 }
|
|
3672
|
+
],
|
|
3673
|
+
// Skip section styles - this block uses theme-based styling
|
|
3674
|
+
skipSectionStyles: true,
|
|
3675
|
+
layout: footerLayout,
|
|
3676
|
+
description: "Site-wide footer with navigation links and optional columns.",
|
|
3677
|
+
styleTokens: {
|
|
3678
|
+
spacing: "md"
|
|
3679
|
+
},
|
|
3680
|
+
paletteHidden: true
|
|
3681
|
+
});
|
|
3682
|
+
var siteFooterBlockDefinition = {
|
|
3683
|
+
manifest: siteFooterManifest
|
|
3684
|
+
};
|
|
3685
|
+
|
|
3686
|
+
// ../blocks/src/system/blocks/testimonials.tsx
|
|
3687
|
+
var testimonialsBackgroundNodes = backgroundLayer("_sectionStyles.background", {
|
|
3688
|
+
imageClassName: "absolute inset-0 -z-10 h-full w-full object-cover opacity-50"
|
|
3689
|
+
});
|
|
3690
|
+
var testimonialsContent = composeFragments([
|
|
3691
|
+
{ fragment: testimonialsHeadingFragment },
|
|
3692
|
+
{ fragment: testimonialsCarouselFragment }
|
|
3693
|
+
]);
|
|
3694
|
+
var testimonialsLayout = section(
|
|
3695
|
+
{ background: "background/base", className: "px-6 py-16 sm:py-20 md:py-24" },
|
|
3696
|
+
// Semantic: comfortable + extra md padding
|
|
3697
|
+
[
|
|
3698
|
+
...testimonialsBackgroundNodes,
|
|
3699
|
+
sectionContainer(
|
|
3700
|
+
[...testimonialsContent.layout],
|
|
3701
|
+
{ gap: "xl", className: "relative" }
|
|
3702
|
+
)
|
|
3703
|
+
]
|
|
3704
|
+
);
|
|
3705
|
+
var fields3 = fieldSchema.array().parse([
|
|
3706
|
+
...testimonialsContent.fields.filter((field) => field.id === "heading" || field.id === "subheading"),
|
|
3707
|
+
...testimonialsContent.fields.filter(
|
|
3708
|
+
(field) => field.id === "slidesToShow" || field.id === "transition" || field.id === "maxEntries"
|
|
3709
|
+
),
|
|
3710
|
+
sectionStylesField({
|
|
3711
|
+
id: "_sectionStyles",
|
|
3712
|
+
label: "Section styles"
|
|
3713
|
+
})
|
|
3714
|
+
]);
|
|
3715
|
+
var dataLoaders2 = buildFragmentDataLoaders(testimonialsContent);
|
|
3716
|
+
var testimonialsManifest = {
|
|
3717
|
+
name: "block.testimonials",
|
|
3718
|
+
version: "0.1.0",
|
|
3719
|
+
title: "Testimonials",
|
|
3720
|
+
description: "Carousel of customer testimonials with optional background styling.",
|
|
3721
|
+
component: "testimonials.carousel",
|
|
3722
|
+
fields: fields3,
|
|
3723
|
+
slots: [],
|
|
3724
|
+
styleTokens: {
|
|
3725
|
+
background: "surface",
|
|
3726
|
+
typography: "body",
|
|
3727
|
+
spacing: "lg"
|
|
3728
|
+
},
|
|
3729
|
+
behaviours: {
|
|
3730
|
+
supportsThemeSwitching: true,
|
|
3731
|
+
inlineEditing: false,
|
|
3732
|
+
animation: true,
|
|
3733
|
+
paletteHidden: false
|
|
3734
|
+
},
|
|
3735
|
+
category: "marketing",
|
|
3736
|
+
tags: ["testimonials", "reviews", "quotes", "feedback", "social-proof", "customers", "carousel"],
|
|
3737
|
+
icon: "MessageSquareQuote",
|
|
3738
|
+
layout: testimonialsLayout
|
|
3739
|
+
};
|
|
3740
|
+
var testimonialsBlockDefinition = {
|
|
3741
|
+
manifest: testimonialsManifest,
|
|
3742
|
+
dataLoaders: dataLoaders2
|
|
3743
|
+
};
|
|
3744
|
+
|
|
3745
|
+
// ../blocks/src/system/blocks/columns.ts
|
|
3746
|
+
var columnsLayout = styledSection({
|
|
3747
|
+
children: sectionContainer(
|
|
3748
|
+
[
|
|
3749
|
+
el(
|
|
3750
|
+
"grid",
|
|
3751
|
+
{
|
|
3752
|
+
cols: {
|
|
3753
|
+
$bind: {
|
|
3754
|
+
from: "content.columns",
|
|
3755
|
+
transforms: [{ id: "array.length" }]
|
|
3756
|
+
}
|
|
3757
|
+
},
|
|
3758
|
+
gap: { $bind: { from: "content.gap" } }
|
|
3759
|
+
},
|
|
3760
|
+
[
|
|
3761
|
+
{
|
|
3762
|
+
type: "stack",
|
|
3763
|
+
props: { gap: "md", className: "h-full" },
|
|
3764
|
+
children: typeBasedLayout(
|
|
3765
|
+
{
|
|
3766
|
+
card: cardFragment.layout,
|
|
3767
|
+
columnContent: columnContentFragment.layout
|
|
3768
|
+
},
|
|
3769
|
+
{ itemName: "column" }
|
|
3770
|
+
),
|
|
3771
|
+
$repeat: {
|
|
3772
|
+
collection: { from: "content.columns" },
|
|
3773
|
+
itemName: "column"
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
]
|
|
3777
|
+
)
|
|
3778
|
+
],
|
|
3779
|
+
{ gap: "md" }
|
|
3780
|
+
),
|
|
3781
|
+
spacing: "medium"
|
|
3782
|
+
});
|
|
3783
|
+
var columnsManifest = createBlockManifest({
|
|
3784
|
+
id: "block.columns",
|
|
3785
|
+
title: "Columns",
|
|
3786
|
+
category: "layout",
|
|
3787
|
+
component: "columns",
|
|
3788
|
+
// Custom fields for columns configuration
|
|
3789
|
+
additionalFields: [
|
|
3790
|
+
fieldSchema.parse({
|
|
3791
|
+
id: "gap",
|
|
3792
|
+
type: "select",
|
|
3793
|
+
label: "Gap between columns",
|
|
3794
|
+
defaultValue: "lg",
|
|
3795
|
+
options: [
|
|
3796
|
+
{ value: "sm", label: "Small" },
|
|
3797
|
+
{ value: "md", label: "Medium" },
|
|
3798
|
+
{ value: "lg", label: "Large" }
|
|
3799
|
+
]
|
|
3800
|
+
}),
|
|
3801
|
+
fragmentsToRepeaterField(
|
|
3802
|
+
"columns",
|
|
3803
|
+
"Columns",
|
|
3804
|
+
{
|
|
3805
|
+
card: cardFragment,
|
|
3806
|
+
columnContent: columnContentFragment
|
|
3807
|
+
},
|
|
3808
|
+
{
|
|
3809
|
+
minItems: 0,
|
|
3810
|
+
maxItems: 4,
|
|
3811
|
+
itemLabel: "Column",
|
|
3812
|
+
description: "Add or remove columns (up to 4)"
|
|
3813
|
+
}
|
|
3814
|
+
)
|
|
3815
|
+
],
|
|
3816
|
+
layout: columnsLayout,
|
|
3817
|
+
description: "Multi-column layout with customizable content per column",
|
|
3818
|
+
tags: ["columns", "grid", "layout", "flexible", "multi-column"],
|
|
3819
|
+
icon: "Columns",
|
|
3820
|
+
styleTokens: {
|
|
3821
|
+
spacing: "lg"
|
|
3822
|
+
}
|
|
3823
|
+
});
|
|
3824
|
+
var columnsBlockDefinition = {
|
|
3825
|
+
manifest: columnsManifest
|
|
3826
|
+
};
|
|
3827
|
+
var appointmentBookingManifest = {
|
|
3828
|
+
name: "block.appointment-booking",
|
|
3829
|
+
version: "2.0.0",
|
|
3830
|
+
// Major version bump - breaking change from v1
|
|
3831
|
+
title: "Appointment Booking",
|
|
3832
|
+
titleSource: "heading",
|
|
3833
|
+
description: "Multi-step appointment booking with customizable form fields",
|
|
3834
|
+
component: "appointment-booking.block",
|
|
3835
|
+
fields: [
|
|
3836
|
+
{
|
|
3837
|
+
id: "formId",
|
|
3838
|
+
type: "reference",
|
|
3839
|
+
label: "Booking Form",
|
|
3840
|
+
description: "Select which booking form to use for collecting customer information",
|
|
3841
|
+
required: true,
|
|
3842
|
+
referenceKind: "bookingForm",
|
|
3843
|
+
allowManualEntry: false
|
|
3844
|
+
},
|
|
3845
|
+
{
|
|
3846
|
+
id: "heading",
|
|
3847
|
+
type: "text",
|
|
3848
|
+
label: "Heading",
|
|
3849
|
+
description: "Main heading shown at the top of the booking flow",
|
|
3850
|
+
required: false,
|
|
3851
|
+
multiline: false,
|
|
3852
|
+
defaultValue: "Book an Appointment"
|
|
3853
|
+
},
|
|
3854
|
+
{
|
|
3855
|
+
id: "description",
|
|
3856
|
+
type: "richText",
|
|
3857
|
+
label: "Description",
|
|
3858
|
+
description: "Optional description or instructions for users",
|
|
3859
|
+
required: false,
|
|
3860
|
+
format: "markdown"
|
|
3861
|
+
}
|
|
3862
|
+
],
|
|
3863
|
+
slots: [],
|
|
3864
|
+
styleTokens: { background: "surface", typography: "body", spacing: "md" },
|
|
3865
|
+
behaviours: { supportsThemeSwitching: true, inlineEditing: false, animation: false, paletteHidden: false },
|
|
3866
|
+
category: "interactive",
|
|
3867
|
+
tags: ["booking", "appointment", "calendar", "scheduling", "reservation"],
|
|
3868
|
+
icon: "Calendar",
|
|
3869
|
+
layout: [
|
|
3870
|
+
styledSection({
|
|
3871
|
+
children: sectionContainer([
|
|
3872
|
+
// Optional heading
|
|
3873
|
+
text(
|
|
3874
|
+
{ as: "h2", size: "2xl", weight: "bold" },
|
|
3875
|
+
bind("content.heading"),
|
|
3876
|
+
when("content.heading")
|
|
3877
|
+
),
|
|
3878
|
+
// Optional description
|
|
3879
|
+
richText(
|
|
3880
|
+
{},
|
|
3881
|
+
bind("content.description"),
|
|
3882
|
+
when("content.description")
|
|
3883
|
+
),
|
|
3884
|
+
// The booking form component - loads booking form by ID and renders multi-step flow
|
|
3885
|
+
bookingForm({
|
|
3886
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
3887
|
+
formId: { $bind: { from: "content.formId" } },
|
|
3888
|
+
form: { $bind: { from: "data.form" } },
|
|
3889
|
+
services: { $bind: { from: "data.services" } }
|
|
3890
|
+
})
|
|
3891
|
+
], {
|
|
3892
|
+
gap: "lg"
|
|
3893
|
+
}),
|
|
3894
|
+
spacing: "spacious"
|
|
3895
|
+
})
|
|
3896
|
+
]
|
|
3897
|
+
};
|
|
3898
|
+
var availabilityDataSchema = zod.z.object({
|
|
3899
|
+
slots: zod.z.array(zod.z.object({
|
|
3900
|
+
startAt: zod.z.string(),
|
|
3901
|
+
endAt: zod.z.string(),
|
|
3902
|
+
resourceId: zod.z.string()
|
|
3903
|
+
}))
|
|
3904
|
+
});
|
|
3905
|
+
var appointmentBookingBlockDefinition = {
|
|
3906
|
+
manifest: appointmentBookingManifest,
|
|
3907
|
+
dataSchemas: { availability: availabilityDataSchema.optional() },
|
|
3908
|
+
dataLoaders: {
|
|
3909
|
+
form: {
|
|
3910
|
+
endpoint: "getPublicFormById",
|
|
3911
|
+
params: {
|
|
3912
|
+
formId: { $bind: { from: "content.formId" } }
|
|
3913
|
+
},
|
|
3914
|
+
mode: "server"
|
|
3915
|
+
},
|
|
3916
|
+
services: {
|
|
3917
|
+
endpoint: "getPublicBookingServices",
|
|
3918
|
+
params: {
|
|
3919
|
+
siteId: { $bind: { from: "$root.siteId" } }
|
|
3920
|
+
},
|
|
3921
|
+
mode: "server"
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
};
|
|
3925
|
+
var eventVenueSchema = zod.z.object({
|
|
3926
|
+
id: zod.z.string(),
|
|
3927
|
+
name: zod.z.string(),
|
|
3928
|
+
address: zod.z.string().nullable()
|
|
3929
|
+
});
|
|
3930
|
+
var publicEventSchema = zod.z.object({
|
|
3931
|
+
id: zod.z.string(),
|
|
3932
|
+
seriesId: zod.z.string(),
|
|
3933
|
+
title: zod.z.string(),
|
|
3934
|
+
description: zod.z.string().nullable(),
|
|
3935
|
+
slug: zod.z.string(),
|
|
3936
|
+
startsAt: zod.z.string(),
|
|
3937
|
+
endsAt: zod.z.string(),
|
|
3938
|
+
capacity: zod.z.number().nullable(),
|
|
3939
|
+
registeredCount: zod.z.number(),
|
|
3940
|
+
availableSpots: zod.z.number().nullable(),
|
|
3941
|
+
venue: eventVenueSchema.nullable()
|
|
3942
|
+
});
|
|
3943
|
+
var publicEventsArraySchema = zod.z.array(publicEventSchema);
|
|
3944
|
+
|
|
3945
|
+
// ../blocks/src/system/blocks/events/shared/fields.ts
|
|
3946
|
+
var cardStylingFields = [
|
|
3947
|
+
{
|
|
3948
|
+
id: "cardVariant",
|
|
3949
|
+
type: "select",
|
|
3950
|
+
label: "Card style",
|
|
3951
|
+
description: "Choose a card style from your theme",
|
|
3952
|
+
required: false,
|
|
3953
|
+
multiple: false,
|
|
3954
|
+
defaultValue: "default",
|
|
3955
|
+
options: [
|
|
3956
|
+
{ value: "default", label: "Default" },
|
|
3957
|
+
{ value: "variant1", label: "Variant 1" },
|
|
3958
|
+
{ value: "variant2", label: "Variant 2" }
|
|
3959
|
+
]
|
|
3960
|
+
},
|
|
3961
|
+
{
|
|
3962
|
+
id: "buttonVariant",
|
|
3963
|
+
type: "select",
|
|
3964
|
+
label: "Button style",
|
|
3965
|
+
description: "Choose a button style from your theme",
|
|
3966
|
+
required: false,
|
|
3967
|
+
multiple: false,
|
|
3968
|
+
defaultValue: "primary",
|
|
3969
|
+
options: [
|
|
3970
|
+
{ value: "primary", label: "Primary" },
|
|
3971
|
+
{ value: "secondary", label: "Secondary" },
|
|
3972
|
+
{ value: "outline", label: "Outline" },
|
|
3973
|
+
{ value: "link", label: "Link" }
|
|
3974
|
+
]
|
|
3975
|
+
},
|
|
3976
|
+
{
|
|
3977
|
+
id: "buttonText",
|
|
3978
|
+
type: "text",
|
|
3979
|
+
label: "Button text",
|
|
3980
|
+
description: "Text for the event action button",
|
|
3981
|
+
required: false,
|
|
3982
|
+
multiline: false,
|
|
3983
|
+
defaultValue: "View event",
|
|
3984
|
+
maxLength: 40
|
|
3985
|
+
}
|
|
3986
|
+
];
|
|
3987
|
+
var eventDisplayFields = [
|
|
3988
|
+
{
|
|
3989
|
+
id: "showVenue",
|
|
3990
|
+
type: "boolean",
|
|
3991
|
+
label: "Show venue",
|
|
3992
|
+
description: "Display venue name and address",
|
|
3993
|
+
required: false,
|
|
3994
|
+
defaultValue: true
|
|
3995
|
+
},
|
|
3996
|
+
{
|
|
3997
|
+
id: "showCapacity",
|
|
3998
|
+
type: "boolean",
|
|
3999
|
+
label: "Show available spots",
|
|
4000
|
+
description: "Display remaining capacity",
|
|
4001
|
+
required: false,
|
|
4002
|
+
defaultValue: true
|
|
4003
|
+
}
|
|
4004
|
+
];
|
|
4005
|
+
var emptyStateField = {
|
|
4006
|
+
id: "emptyMessage",
|
|
4007
|
+
type: "text",
|
|
4008
|
+
label: "Empty state message",
|
|
4009
|
+
description: "Message when no upcoming events",
|
|
4010
|
+
required: false,
|
|
4011
|
+
multiline: false,
|
|
4012
|
+
defaultValue: "No upcoming events scheduled.",
|
|
4013
|
+
maxLength: 200
|
|
4014
|
+
};
|
|
4015
|
+
var sectionHeaderFields = (defaultHeading) => [
|
|
4016
|
+
{
|
|
4017
|
+
id: "heading",
|
|
4018
|
+
type: "text",
|
|
4019
|
+
label: "Heading",
|
|
4020
|
+
description: "Main heading for the events section",
|
|
4021
|
+
required: false,
|
|
4022
|
+
multiline: false,
|
|
4023
|
+
defaultValue: defaultHeading
|
|
4024
|
+
},
|
|
4025
|
+
{
|
|
4026
|
+
id: "description",
|
|
4027
|
+
type: "richText",
|
|
4028
|
+
label: "Description",
|
|
4029
|
+
description: "Optional introductory text",
|
|
4030
|
+
required: false,
|
|
4031
|
+
format: "markdown"
|
|
4032
|
+
}
|
|
4033
|
+
];
|
|
4034
|
+
var layoutField = {
|
|
4035
|
+
id: "layout",
|
|
4036
|
+
type: "select",
|
|
4037
|
+
label: "Layout",
|
|
4038
|
+
description: "Choose how events are displayed",
|
|
4039
|
+
required: false,
|
|
4040
|
+
multiple: false,
|
|
4041
|
+
defaultValue: "grid",
|
|
4042
|
+
options: [
|
|
4043
|
+
{ value: "grid", label: "Grid (cards)" },
|
|
4044
|
+
{ value: "stack", label: "Stack (vertical list)" }
|
|
4045
|
+
]
|
|
4046
|
+
};
|
|
4047
|
+
var columnsField = (options = ["2", "3", "4"]) => ({
|
|
4048
|
+
id: "columns",
|
|
4049
|
+
type: "select",
|
|
4050
|
+
label: "Columns",
|
|
4051
|
+
description: "Number of columns in grid layout",
|
|
4052
|
+
required: false,
|
|
4053
|
+
multiple: false,
|
|
4054
|
+
defaultValue: "3",
|
|
4055
|
+
ui: {
|
|
4056
|
+
visibleWhen: { field: "layout", equals: "grid" }
|
|
4057
|
+
},
|
|
4058
|
+
options: options.map((n) => ({ value: n, label: `${n} columns` }))
|
|
4059
|
+
});
|
|
4060
|
+
|
|
4061
|
+
// ../blocks/src/system/blocks/event-registration.ts
|
|
4062
|
+
var eventRegistrationManifest = {
|
|
4063
|
+
name: "block.event-registration",
|
|
4064
|
+
version: "1.0.0",
|
|
4065
|
+
title: "Event Registration",
|
|
4066
|
+
titleSource: "heading",
|
|
4067
|
+
description: "Multi-step event registration form",
|
|
4068
|
+
component: "event-registration.block",
|
|
4069
|
+
fields: [
|
|
4070
|
+
{
|
|
4071
|
+
id: "heading",
|
|
4072
|
+
type: "text",
|
|
4073
|
+
label: "Heading",
|
|
4074
|
+
description: "Main heading for the registration form",
|
|
4075
|
+
required: false,
|
|
4076
|
+
multiline: false,
|
|
4077
|
+
defaultValue: "Register for this event"
|
|
4078
|
+
},
|
|
4079
|
+
{
|
|
4080
|
+
id: "description",
|
|
4081
|
+
type: "richText",
|
|
4082
|
+
label: "Description",
|
|
4083
|
+
description: "Optional introductory text",
|
|
4084
|
+
required: false,
|
|
4085
|
+
format: "markdown"
|
|
4086
|
+
},
|
|
4087
|
+
{
|
|
4088
|
+
id: "maxTickets",
|
|
4089
|
+
type: "select",
|
|
4090
|
+
label: "Max tickets per registration",
|
|
4091
|
+
description: "Maximum tickets a person can register for",
|
|
4092
|
+
required: false,
|
|
4093
|
+
multiple: false,
|
|
4094
|
+
defaultValue: "5",
|
|
4095
|
+
options: [
|
|
4096
|
+
{ value: "1", label: "1 ticket" },
|
|
4097
|
+
{ value: "2", label: "2 tickets" },
|
|
4098
|
+
{ value: "3", label: "3 tickets" },
|
|
4099
|
+
{ value: "5", label: "5 tickets" },
|
|
4100
|
+
{ value: "10", label: "10 tickets" }
|
|
4101
|
+
]
|
|
4102
|
+
},
|
|
4103
|
+
{
|
|
4104
|
+
id: "showVenue",
|
|
4105
|
+
type: "boolean",
|
|
4106
|
+
label: "Show venue",
|
|
4107
|
+
description: "Display venue information",
|
|
4108
|
+
required: false,
|
|
4109
|
+
defaultValue: true
|
|
4110
|
+
},
|
|
4111
|
+
{
|
|
4112
|
+
id: "showCapacity",
|
|
4113
|
+
type: "boolean",
|
|
4114
|
+
label: "Show available spots",
|
|
4115
|
+
description: "Display remaining capacity",
|
|
4116
|
+
required: false,
|
|
4117
|
+
defaultValue: true
|
|
4118
|
+
},
|
|
4119
|
+
{
|
|
4120
|
+
id: "successMessage",
|
|
4121
|
+
type: "text",
|
|
4122
|
+
label: "Success message",
|
|
4123
|
+
description: "Message shown after successful registration",
|
|
4124
|
+
required: false,
|
|
4125
|
+
multiline: true,
|
|
4126
|
+
defaultValue: "Thank you for registering! Check your email for confirmation details.",
|
|
4127
|
+
maxLength: 500
|
|
4128
|
+
},
|
|
4129
|
+
{
|
|
4130
|
+
id: "waitlistMessage",
|
|
4131
|
+
type: "text",
|
|
4132
|
+
label: "Waitlist message",
|
|
4133
|
+
description: "Message when added to waitlist",
|
|
4134
|
+
required: false,
|
|
4135
|
+
multiline: true,
|
|
4136
|
+
defaultValue: "You've been added to the waitlist. We'll notify you if a spot opens up.",
|
|
4137
|
+
maxLength: 500
|
|
4138
|
+
},
|
|
4139
|
+
{
|
|
4140
|
+
id: "buttonText",
|
|
4141
|
+
type: "text",
|
|
4142
|
+
label: "Submit button text",
|
|
4143
|
+
description: "Text for the registration submit button",
|
|
4144
|
+
required: false,
|
|
4145
|
+
multiline: false,
|
|
4146
|
+
defaultValue: "Complete Registration",
|
|
4147
|
+
maxLength: 40
|
|
4148
|
+
},
|
|
4149
|
+
{
|
|
4150
|
+
id: "buttonVariant",
|
|
4151
|
+
type: "select",
|
|
4152
|
+
label: "Button style",
|
|
4153
|
+
description: "Choose a button style from your theme",
|
|
4154
|
+
required: false,
|
|
4155
|
+
multiple: false,
|
|
4156
|
+
defaultValue: "primary",
|
|
4157
|
+
options: [
|
|
4158
|
+
{ value: "primary", label: "Primary" },
|
|
4159
|
+
{ value: "secondary", label: "Secondary" },
|
|
4160
|
+
{ value: "outline", label: "Outline" }
|
|
4161
|
+
]
|
|
4162
|
+
}
|
|
4163
|
+
],
|
|
4164
|
+
slots: [],
|
|
4165
|
+
styleTokens: { background: "surface", typography: "body", spacing: "md" },
|
|
4166
|
+
behaviours: { supportsThemeSwitching: true, inlineEditing: false, animation: false, paletteHidden: false },
|
|
4167
|
+
category: "interactive",
|
|
4168
|
+
tags: ["events", "registration", "booking", "form", "signup"],
|
|
4169
|
+
icon: "ClipboardList",
|
|
4170
|
+
layout: [
|
|
4171
|
+
styledSection({
|
|
4172
|
+
spacing: "spacious",
|
|
4173
|
+
children: sectionContainer([
|
|
4174
|
+
// Optional heading
|
|
4175
|
+
text(
|
|
4176
|
+
{ as: "h2", size: "2xl", weight: "bold" },
|
|
4177
|
+
bind("content.heading"),
|
|
4178
|
+
when("content.heading")
|
|
4179
|
+
),
|
|
4180
|
+
// Optional description
|
|
4181
|
+
richText(
|
|
4182
|
+
{},
|
|
4183
|
+
bind("content.description"),
|
|
4184
|
+
when("content.description")
|
|
4185
|
+
),
|
|
4186
|
+
// Event registration form component
|
|
4187
|
+
eventRegistration({
|
|
4188
|
+
// Site context for API calls
|
|
4189
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
4190
|
+
// Pre-selected occurrence from route context (if available)
|
|
4191
|
+
occurrenceContext: { $bind: { from: "$root.occurrenceContext" } },
|
|
4192
|
+
// Event data from content entry context (if on event template)
|
|
4193
|
+
contentEntry: { $bind: { from: "$root.contentEntry" } },
|
|
4194
|
+
// Content configuration
|
|
4195
|
+
maxTickets: { $bind: { from: "content.maxTickets", fallback: "5" } },
|
|
4196
|
+
showVenue: { $bind: { from: "content.showVenue" } },
|
|
4197
|
+
showCapacity: { $bind: { from: "content.showCapacity" } },
|
|
4198
|
+
successMessage: { $bind: { from: "content.successMessage" } },
|
|
4199
|
+
waitlistMessage: { $bind: { from: "content.waitlistMessage" } },
|
|
4200
|
+
buttonText: { $bind: { from: "content.buttonText", fallback: "Complete Registration" } },
|
|
4201
|
+
buttonVariant: { $bind: { from: "content.buttonVariant", fallback: "primary" } },
|
|
4202
|
+
// Event data from loader
|
|
4203
|
+
events: { $bind: { from: "data.events" } }
|
|
4204
|
+
})
|
|
4205
|
+
], {
|
|
4206
|
+
gap: "lg"
|
|
4207
|
+
})
|
|
4208
|
+
})
|
|
4209
|
+
]
|
|
4210
|
+
};
|
|
4211
|
+
var occurrenceContextSchema = zod.z.object({
|
|
4212
|
+
/** Unique identifier for this occurrence */
|
|
4213
|
+
id: zod.z.string(),
|
|
4214
|
+
/** The event series this occurrence belongs to */
|
|
4215
|
+
seriesId: zod.z.string(),
|
|
4216
|
+
/** ISO 8601 datetime when the occurrence starts */
|
|
4217
|
+
startsAt: zod.z.string(),
|
|
4218
|
+
/** ISO 8601 datetime when the occurrence ends */
|
|
4219
|
+
endsAt: zod.z.string(),
|
|
4220
|
+
/** Override capacity for this specific occurrence (null = use series default) */
|
|
4221
|
+
capacityOverride: zod.z.number().nullable().optional(),
|
|
4222
|
+
/** Field-level overrides: { title?, description?, venueId?, etc. } */
|
|
4223
|
+
overrides: zod.z.record(zod.z.string(), zod.z.unknown()).nullable().optional()
|
|
4224
|
+
}).nullable();
|
|
4225
|
+
var eventRegistrationBlockDefinition = {
|
|
4226
|
+
manifest: eventRegistrationManifest,
|
|
4227
|
+
dataSchemas: {
|
|
4228
|
+
events: zod.z.array(publicEventSchema).optional(),
|
|
4229
|
+
occurrenceContext: occurrenceContextSchema.optional()
|
|
4230
|
+
},
|
|
4231
|
+
dataLoaders: {
|
|
4232
|
+
// Load events for occurrence selection
|
|
4233
|
+
// This is needed when block is on a standalone page or event template without occurrence in URL
|
|
4234
|
+
events: {
|
|
4235
|
+
endpoint: "listPublicEvents",
|
|
4236
|
+
params: {
|
|
4237
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
4238
|
+
limit: "50",
|
|
4239
|
+
// Get more events for selection
|
|
4240
|
+
stage: { $bind: { from: "$root.previewStage", fallback: "published" } }
|
|
4241
|
+
},
|
|
4242
|
+
mode: "server"
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
};
|
|
4246
|
+
|
|
4247
|
+
// ../blocks/src/system/blocks/events/event-spotlight.ts
|
|
4248
|
+
var eventSpotlightManifest = {
|
|
4249
|
+
name: "block.event-spotlight",
|
|
4250
|
+
version: "1.0.0",
|
|
4251
|
+
title: "Event Spotlight",
|
|
4252
|
+
titleSource: "heading",
|
|
4253
|
+
description: "Feature a few upcoming events on your homepage",
|
|
4254
|
+
component: "event-spotlight.block",
|
|
4255
|
+
fields: [
|
|
4256
|
+
...sectionHeaderFields("Upcoming Events"),
|
|
4257
|
+
{
|
|
4258
|
+
id: "maxEvents",
|
|
4259
|
+
type: "select",
|
|
4260
|
+
label: "Events to display",
|
|
4261
|
+
description: "Number of events to show",
|
|
4262
|
+
required: false,
|
|
4263
|
+
multiple: false,
|
|
4264
|
+
defaultValue: "3",
|
|
4265
|
+
options: [
|
|
4266
|
+
{ value: "1", label: "1 event" },
|
|
4267
|
+
{ value: "2", label: "2 events" },
|
|
4268
|
+
{ value: "3", label: "3 events" },
|
|
4269
|
+
{ value: "4", label: "4 events" },
|
|
4270
|
+
{ value: "5", label: "5 events" },
|
|
4271
|
+
{ value: "6", label: "6 events" }
|
|
4272
|
+
]
|
|
4273
|
+
},
|
|
4274
|
+
layoutField,
|
|
4275
|
+
columnsField(["2", "3"]),
|
|
4276
|
+
...cardStylingFields,
|
|
4277
|
+
...eventDisplayFields,
|
|
4278
|
+
emptyStateField
|
|
4279
|
+
],
|
|
4280
|
+
slots: [],
|
|
4281
|
+
styleTokens: { background: "surface", typography: "body", spacing: "md" },
|
|
4282
|
+
behaviours: { supportsThemeSwitching: true, inlineEditing: false, animation: false, paletteHidden: false },
|
|
4283
|
+
category: "interactive",
|
|
4284
|
+
tags: ["events", "featured", "homepage", "spotlight", "upcoming"],
|
|
4285
|
+
icon: "Star",
|
|
4286
|
+
layout: [
|
|
4287
|
+
styledSection({
|
|
4288
|
+
children: sectionContainer([
|
|
4289
|
+
// Optional heading
|
|
4290
|
+
text(
|
|
4291
|
+
{ as: "h2", size: "2xl", weight: "bold" },
|
|
4292
|
+
bind("content.heading"),
|
|
4293
|
+
when("content.heading")
|
|
4294
|
+
),
|
|
4295
|
+
// Optional description
|
|
4296
|
+
richText(
|
|
4297
|
+
{},
|
|
4298
|
+
bind("content.description"),
|
|
4299
|
+
when("content.description")
|
|
4300
|
+
),
|
|
4301
|
+
// Event spotlight component
|
|
4302
|
+
el("event-spotlight", {
|
|
4303
|
+
events: { $bind: { from: "data.events" } },
|
|
4304
|
+
layout: { $bind: { from: "content.layout", fallback: "grid" } },
|
|
4305
|
+
columns: { $bind: { from: "content.columns", fallback: "3" } },
|
|
4306
|
+
cardVariant: { $bind: { from: "content.cardVariant", fallback: "default" } },
|
|
4307
|
+
buttonVariant: { $bind: { from: "content.buttonVariant", fallback: "primary" } },
|
|
4308
|
+
buttonText: { $bind: { from: "content.buttonText", fallback: "View event" } },
|
|
4309
|
+
showVenue: { $bind: { from: "content.showVenue" } },
|
|
4310
|
+
showCapacity: { $bind: { from: "content.showCapacity" } },
|
|
4311
|
+
emptyMessage: { $bind: { from: "content.emptyMessage" } }
|
|
4312
|
+
})
|
|
4313
|
+
], {
|
|
4314
|
+
gap: "lg"
|
|
4315
|
+
}),
|
|
4316
|
+
spacing: "spacious"
|
|
4317
|
+
})
|
|
4318
|
+
]
|
|
4319
|
+
};
|
|
4320
|
+
var eventSpotlightBlockDefinition = {
|
|
4321
|
+
manifest: eventSpotlightManifest,
|
|
4322
|
+
dataSchemas: {
|
|
4323
|
+
events: publicEventsArraySchema.optional()
|
|
4324
|
+
},
|
|
4325
|
+
dataLoaders: {
|
|
4326
|
+
events: {
|
|
4327
|
+
endpoint: "listPublicEvents",
|
|
4328
|
+
params: {
|
|
4329
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
4330
|
+
limit: { $bind: { from: "content.maxEvents", fallback: "3" } },
|
|
4331
|
+
stage: { $bind: { from: "$root.previewStage", fallback: "published" } }
|
|
4332
|
+
},
|
|
4333
|
+
mode: "server"
|
|
4334
|
+
}
|
|
4335
|
+
}
|
|
4336
|
+
};
|
|
4337
|
+
|
|
4338
|
+
// ../blocks/src/system/blocks/events/event-listing.ts
|
|
4339
|
+
var eventListingManifest = {
|
|
4340
|
+
name: "block.event-listing",
|
|
4341
|
+
version: "1.0.0",
|
|
4342
|
+
title: "Event Listing",
|
|
4343
|
+
titleSource: "heading",
|
|
4344
|
+
description: "Paginated list of all upcoming events",
|
|
4345
|
+
component: "event-listing.block",
|
|
4346
|
+
fields: [
|
|
4347
|
+
...sectionHeaderFields("All Events"),
|
|
4348
|
+
layoutField,
|
|
4349
|
+
columnsField(["2", "3", "4"]),
|
|
4350
|
+
{
|
|
4351
|
+
id: "eventsPerPage",
|
|
4352
|
+
type: "select",
|
|
4353
|
+
label: "Events per page",
|
|
4354
|
+
description: "Number of events to load at a time",
|
|
4355
|
+
required: false,
|
|
4356
|
+
multiple: false,
|
|
4357
|
+
defaultValue: "12",
|
|
4358
|
+
options: [
|
|
4359
|
+
{ value: "6", label: "6 events" },
|
|
4360
|
+
{ value: "12", label: "12 events" },
|
|
4361
|
+
{ value: "24", label: "24 events" }
|
|
4362
|
+
]
|
|
4363
|
+
},
|
|
4364
|
+
{
|
|
4365
|
+
id: "loadMoreText",
|
|
4366
|
+
type: "text",
|
|
4367
|
+
label: "Load more button text",
|
|
4368
|
+
description: "Text for the pagination button",
|
|
4369
|
+
required: false,
|
|
4370
|
+
multiline: false,
|
|
4371
|
+
defaultValue: "Load more events",
|
|
4372
|
+
maxLength: 40
|
|
4373
|
+
},
|
|
4374
|
+
...cardStylingFields,
|
|
4375
|
+
...eventDisplayFields,
|
|
4376
|
+
emptyStateField
|
|
4377
|
+
],
|
|
4378
|
+
slots: [],
|
|
4379
|
+
styleTokens: { background: "surface", typography: "body", spacing: "md" },
|
|
4380
|
+
behaviours: { supportsThemeSwitching: true, inlineEditing: false, animation: false, paletteHidden: false },
|
|
4381
|
+
category: "interactive",
|
|
4382
|
+
tags: ["events", "listing", "paginated", "archive", "all-events"],
|
|
4383
|
+
icon: "List",
|
|
4384
|
+
layout: [
|
|
4385
|
+
styledSection({
|
|
4386
|
+
children: sectionContainer([
|
|
4387
|
+
// Optional heading
|
|
4388
|
+
text(
|
|
4389
|
+
{ as: "h2", size: "2xl", weight: "bold" },
|
|
4390
|
+
bind("content.heading"),
|
|
4391
|
+
when("content.heading")
|
|
4392
|
+
),
|
|
4393
|
+
// Optional description
|
|
4394
|
+
richText(
|
|
4395
|
+
{},
|
|
4396
|
+
bind("content.description"),
|
|
4397
|
+
when("content.description")
|
|
4398
|
+
),
|
|
4399
|
+
// Event listing component with pagination
|
|
4400
|
+
el("event-listing", {
|
|
4401
|
+
events: { $bind: { from: "data.events" } },
|
|
4402
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
4403
|
+
layout: { $bind: { from: "content.layout", fallback: "grid" } },
|
|
4404
|
+
columns: { $bind: { from: "content.columns", fallback: "3" } },
|
|
4405
|
+
eventsPerPage: { $bind: { from: "content.eventsPerPage", fallback: "12" } },
|
|
4406
|
+
loadMoreText: { $bind: { from: "content.loadMoreText", fallback: "Load more events" } },
|
|
4407
|
+
cardVariant: { $bind: { from: "content.cardVariant", fallback: "default" } },
|
|
4408
|
+
buttonVariant: { $bind: { from: "content.buttonVariant", fallback: "primary" } },
|
|
4409
|
+
buttonText: { $bind: { from: "content.buttonText", fallback: "View event" } },
|
|
4410
|
+
showVenue: { $bind: { from: "content.showVenue" } },
|
|
4411
|
+
showCapacity: { $bind: { from: "content.showCapacity" } },
|
|
4412
|
+
emptyMessage: { $bind: { from: "content.emptyMessage" } }
|
|
4413
|
+
})
|
|
4414
|
+
], {
|
|
4415
|
+
gap: "lg"
|
|
4416
|
+
}),
|
|
4417
|
+
spacing: "spacious"
|
|
4418
|
+
})
|
|
4419
|
+
]
|
|
4420
|
+
};
|
|
4421
|
+
var eventListingBlockDefinition = {
|
|
4422
|
+
manifest: eventListingManifest,
|
|
4423
|
+
dataSchemas: {
|
|
4424
|
+
events: publicEventsArraySchema.optional()
|
|
4425
|
+
},
|
|
4426
|
+
dataLoaders: {
|
|
4427
|
+
events: {
|
|
4428
|
+
endpoint: "listPublicEvents",
|
|
4429
|
+
params: {
|
|
4430
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
4431
|
+
limit: { $bind: { from: "content.eventsPerPage", fallback: "12" } },
|
|
4432
|
+
stage: { $bind: { from: "$root.previewStage", fallback: "published" } }
|
|
4433
|
+
},
|
|
4434
|
+
mode: "server"
|
|
4435
|
+
}
|
|
4436
|
+
}
|
|
4437
|
+
};
|
|
4438
|
+
|
|
4439
|
+
// ../blocks/src/system/blocks/events/event-calendar.ts
|
|
4440
|
+
var eventCalendarManifest = {
|
|
4441
|
+
name: "block.event-calendar",
|
|
4442
|
+
version: "2.0.0",
|
|
4443
|
+
title: "Event Calendar",
|
|
4444
|
+
titleSource: "heading",
|
|
4445
|
+
description: "Interactive calendar showing events by month or week",
|
|
4446
|
+
component: "event-calendar.block",
|
|
4447
|
+
fields: [
|
|
4448
|
+
...sectionHeaderFields("Event Calendar"),
|
|
4449
|
+
{
|
|
4450
|
+
id: "calendarView",
|
|
4451
|
+
type: "select",
|
|
4452
|
+
label: "Default view",
|
|
4453
|
+
description: "Initial calendar view",
|
|
4454
|
+
required: false,
|
|
4455
|
+
multiple: false,
|
|
4456
|
+
defaultValue: "month",
|
|
4457
|
+
options: [
|
|
4458
|
+
{ value: "month", label: "Month" },
|
|
4459
|
+
{ value: "week", label: "Week" }
|
|
4460
|
+
]
|
|
4461
|
+
},
|
|
4462
|
+
{
|
|
4463
|
+
id: "startOfWeek",
|
|
4464
|
+
type: "select",
|
|
4465
|
+
label: "Week starts on",
|
|
4466
|
+
description: "First day of the week",
|
|
4467
|
+
required: false,
|
|
4468
|
+
multiple: false,
|
|
4469
|
+
defaultValue: "monday",
|
|
4470
|
+
options: [
|
|
4471
|
+
{ value: "sunday", label: "Sunday" },
|
|
4472
|
+
{ value: "monday", label: "Monday" }
|
|
4473
|
+
]
|
|
4474
|
+
},
|
|
4475
|
+
...cardStylingFields.filter((f) => f.id === "buttonVariant"),
|
|
4476
|
+
// Only button variant for calendar
|
|
4477
|
+
emptyStateField
|
|
4478
|
+
],
|
|
4479
|
+
slots: [],
|
|
4480
|
+
styleTokens: { background: "surface", typography: "body", spacing: "md" },
|
|
4481
|
+
behaviours: { supportsThemeSwitching: true, inlineEditing: false, animation: false, paletteHidden: false },
|
|
4482
|
+
category: "interactive",
|
|
4483
|
+
tags: ["events", "calendar", "month", "week", "schedule", "interactive"],
|
|
4484
|
+
icon: "CalendarDays",
|
|
4485
|
+
layout: [
|
|
4486
|
+
styledSection({
|
|
4487
|
+
children: sectionContainer([
|
|
4488
|
+
// Optional heading
|
|
4489
|
+
text(
|
|
4490
|
+
{ as: "h2", size: "2xl", weight: "bold" },
|
|
4491
|
+
bind("content.heading"),
|
|
4492
|
+
when("content.heading")
|
|
4493
|
+
),
|
|
4494
|
+
// Optional description
|
|
4495
|
+
richText(
|
|
4496
|
+
{},
|
|
4497
|
+
bind("content.description"),
|
|
4498
|
+
when("content.description")
|
|
4499
|
+
),
|
|
4500
|
+
// Event calendar grid component
|
|
4501
|
+
el("event-calendar", {
|
|
4502
|
+
events: { $bind: { from: "data.events" } },
|
|
4503
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
4504
|
+
calendarView: { $bind: { from: "content.calendarView", fallback: "month" } },
|
|
4505
|
+
startOfWeek: { $bind: { from: "content.startOfWeek", fallback: "monday" } },
|
|
4506
|
+
buttonVariant: { $bind: { from: "content.buttonVariant", fallback: "primary" } },
|
|
4507
|
+
emptyMessage: { $bind: { from: "content.emptyMessage" } }
|
|
4508
|
+
})
|
|
4509
|
+
], {
|
|
4510
|
+
gap: "lg"
|
|
4511
|
+
}),
|
|
4512
|
+
spacing: "spacious"
|
|
4513
|
+
})
|
|
4514
|
+
]
|
|
4515
|
+
};
|
|
4516
|
+
var eventCalendarBlockDefinition = {
|
|
4517
|
+
manifest: eventCalendarManifest,
|
|
4518
|
+
dataSchemas: {
|
|
4519
|
+
events: publicEventsArraySchema.optional()
|
|
4520
|
+
},
|
|
4521
|
+
dataLoaders: {
|
|
4522
|
+
events: {
|
|
4523
|
+
endpoint: "listPublicEvents",
|
|
4524
|
+
params: {
|
|
4525
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
4526
|
+
// Pre-fetch 3 months of events (server-side)
|
|
4527
|
+
// The client will use the same API to fetch more as user navigates
|
|
4528
|
+
limit: 100,
|
|
4529
|
+
// High limit for calendar view
|
|
4530
|
+
stage: { $bind: { from: "$root.previewStage", fallback: "published" } }
|
|
4531
|
+
},
|
|
4532
|
+
mode: "server"
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
};
|
|
4536
|
+
var embedFields = [
|
|
4537
|
+
// Section heading
|
|
4538
|
+
fieldSchema.parse({
|
|
4539
|
+
id: "heading",
|
|
4540
|
+
type: "text",
|
|
4541
|
+
label: "Heading",
|
|
4542
|
+
description: "Optional section title displayed above the content.",
|
|
4543
|
+
required: false,
|
|
4544
|
+
maxLength: 120
|
|
4545
|
+
}),
|
|
4546
|
+
fieldSchema.parse({
|
|
4547
|
+
id: "subheading",
|
|
4548
|
+
type: "richText",
|
|
4549
|
+
label: "Subheading",
|
|
4550
|
+
description: "Optional section description below the heading.",
|
|
4551
|
+
required: false,
|
|
4552
|
+
format: "markdown",
|
|
4553
|
+
ui: { variant: "limited" }
|
|
4554
|
+
}),
|
|
4555
|
+
// Content source
|
|
4556
|
+
fieldSchema.parse({
|
|
4557
|
+
id: "contentType",
|
|
4558
|
+
type: "contentTypeSelect",
|
|
4559
|
+
label: "Content type",
|
|
4560
|
+
description: "Select the content type to embed.",
|
|
4561
|
+
required: true,
|
|
4562
|
+
filter: "all"
|
|
4563
|
+
}),
|
|
4564
|
+
fieldSchema.parse({
|
|
4565
|
+
id: "mode",
|
|
4566
|
+
type: "select",
|
|
4567
|
+
label: "Selection mode",
|
|
4568
|
+
description: "How to select which entries to display.",
|
|
4569
|
+
required: true,
|
|
4570
|
+
defaultValue: "query",
|
|
4571
|
+
options: [
|
|
4572
|
+
{ value: "query", label: "Query (automatic)" },
|
|
4573
|
+
{ value: "manual", label: "Manual (pick entries)" }
|
|
4574
|
+
]
|
|
4575
|
+
}),
|
|
4576
|
+
// Query mode options
|
|
4577
|
+
fieldSchema.parse({
|
|
4578
|
+
id: "limit",
|
|
4579
|
+
type: "select",
|
|
4580
|
+
label: "Limit",
|
|
4581
|
+
description: "Maximum number of entries to display.",
|
|
4582
|
+
defaultValue: "10",
|
|
4583
|
+
options: [
|
|
4584
|
+
{ value: "3", label: "3 entries" },
|
|
4585
|
+
{ value: "6", label: "6 entries" },
|
|
4586
|
+
{ value: "10", label: "10 entries" },
|
|
4587
|
+
{ value: "20", label: "20 entries" },
|
|
4588
|
+
{ value: "50", label: "50 entries" }
|
|
4589
|
+
],
|
|
4590
|
+
ui: {
|
|
4591
|
+
visibleWhen: { field: "mode", equals: "query" }
|
|
4592
|
+
}
|
|
4593
|
+
}),
|
|
4594
|
+
fieldSchema.parse({
|
|
4595
|
+
id: "orderBy",
|
|
4596
|
+
type: "select",
|
|
4597
|
+
label: "Order by",
|
|
4598
|
+
description: "How to sort the entries.",
|
|
4599
|
+
defaultValue: "order",
|
|
4600
|
+
options: [
|
|
4601
|
+
{ value: "order", label: "Custom order field" },
|
|
4602
|
+
{ value: "newest", label: "Newest first" },
|
|
4603
|
+
{ value: "oldest", label: "Oldest first" },
|
|
4604
|
+
{ value: "title", label: "Title (A-Z)" }
|
|
4605
|
+
],
|
|
4606
|
+
ui: {
|
|
4607
|
+
visibleWhen: { field: "mode", equals: "query" }
|
|
4608
|
+
}
|
|
4609
|
+
}),
|
|
4610
|
+
// Manual mode options - entry references
|
|
4611
|
+
fieldSchema.parse({
|
|
4612
|
+
id: "entries",
|
|
4613
|
+
type: "repeater",
|
|
4614
|
+
label: "Entries",
|
|
4615
|
+
description: "Select specific entries to display.",
|
|
4616
|
+
required: false,
|
|
4617
|
+
itemLabel: "Entry",
|
|
4618
|
+
minItems: 1,
|
|
4619
|
+
maxItems: 20,
|
|
4620
|
+
schema: {
|
|
4621
|
+
fields: [
|
|
4622
|
+
{
|
|
4623
|
+
id: "entryId",
|
|
4624
|
+
type: "entryPicker",
|
|
4625
|
+
label: "Entry",
|
|
4626
|
+
description: "Select a content entry to embed.",
|
|
4627
|
+
required: true,
|
|
4628
|
+
ui: {
|
|
4629
|
+
contentTypeField: "contentType"
|
|
4630
|
+
}
|
|
4631
|
+
}
|
|
4632
|
+
]
|
|
4633
|
+
},
|
|
4634
|
+
ui: {
|
|
4635
|
+
visibleWhen: { field: "mode", equals: "manual" }
|
|
4636
|
+
}
|
|
4637
|
+
}),
|
|
4638
|
+
// Layout selection - SDK sites can provide custom options via blockFieldOptions
|
|
4639
|
+
fieldSchema.parse({
|
|
4640
|
+
id: "layout",
|
|
4641
|
+
type: "select",
|
|
4642
|
+
label: "Layout",
|
|
4643
|
+
description: "Select a layout style for displaying the embedded content.",
|
|
4644
|
+
defaultValue: "list",
|
|
4645
|
+
options: [
|
|
4646
|
+
{ value: "list", label: "List" },
|
|
4647
|
+
{ value: "grid", label: "Grid" },
|
|
4648
|
+
{ value: "carousel", label: "Carousel" }
|
|
4649
|
+
],
|
|
4650
|
+
ui: {
|
|
4651
|
+
widget: "sdkSelect"
|
|
4652
|
+
// Use SDK-aware widget for site-specific options
|
|
4653
|
+
}
|
|
4654
|
+
}),
|
|
4655
|
+
// Empty state
|
|
4656
|
+
fieldSchema.parse({
|
|
4657
|
+
id: "emptyMessage",
|
|
4658
|
+
type: "text",
|
|
4659
|
+
label: "Empty state message",
|
|
4660
|
+
description: "Message shown when no entries are available.",
|
|
4661
|
+
defaultValue: "No entries found.",
|
|
4662
|
+
maxLength: 200
|
|
4663
|
+
})
|
|
4664
|
+
];
|
|
4665
|
+
var embedLayout = styledSection({
|
|
4666
|
+
children: sectionContainer([
|
|
4667
|
+
// Heading
|
|
4668
|
+
text(
|
|
4669
|
+
{
|
|
4670
|
+
as: "h2",
|
|
4671
|
+
className: "text-3xl font-bold",
|
|
4672
|
+
style: textColorStyle("neutral-900")
|
|
4673
|
+
},
|
|
4674
|
+
bind("content.heading"),
|
|
4675
|
+
when("content.heading")
|
|
4676
|
+
),
|
|
4677
|
+
// Subheading
|
|
4678
|
+
el(
|
|
4679
|
+
"richText",
|
|
4680
|
+
{
|
|
4681
|
+
className: "mt-2 text-lg",
|
|
4682
|
+
style: textColorStyle("neutral-600")
|
|
4683
|
+
},
|
|
4684
|
+
void 0,
|
|
4685
|
+
bind("content.subheading"),
|
|
4686
|
+
when("content.subheading")
|
|
4687
|
+
),
|
|
4688
|
+
// Default entry rendering - a simple list
|
|
4689
|
+
// Sites should override this with custom layouts via blockOverrides
|
|
4690
|
+
// Outer stack provides gap between repeated entry cards
|
|
4691
|
+
stack(
|
|
4692
|
+
{ gap: "md", className: "mt-8" },
|
|
4693
|
+
[
|
|
4694
|
+
// Entry card (repeated for each entry)
|
|
4695
|
+
stack(
|
|
4696
|
+
{
|
|
4697
|
+
gap: "sm",
|
|
4698
|
+
className: "rounded-lg border border-neutral-200 bg-white p-4 shadow-sm"
|
|
4699
|
+
},
|
|
4700
|
+
[
|
|
4701
|
+
text(
|
|
4702
|
+
{
|
|
4703
|
+
as: "h3",
|
|
4704
|
+
className: "text-lg font-semibold",
|
|
4705
|
+
style: textColorStyle("neutral-900")
|
|
4706
|
+
},
|
|
4707
|
+
bind("entry.title")
|
|
4708
|
+
),
|
|
4709
|
+
text(
|
|
4710
|
+
{
|
|
4711
|
+
as: "p",
|
|
4712
|
+
className: "text-sm",
|
|
4713
|
+
style: textColorStyle("neutral-500")
|
|
4714
|
+
},
|
|
4715
|
+
bind("entry.slug"),
|
|
4716
|
+
when("entry.slug")
|
|
4717
|
+
)
|
|
4718
|
+
],
|
|
4719
|
+
repeat("data.entries", "entry")
|
|
4720
|
+
)
|
|
4721
|
+
],
|
|
4722
|
+
when("data.entries")
|
|
4723
|
+
),
|
|
4724
|
+
// Empty state
|
|
4725
|
+
stack(
|
|
4726
|
+
{
|
|
4727
|
+
gap: "sm",
|
|
4728
|
+
className: "py-12 text-center"
|
|
4729
|
+
},
|
|
4730
|
+
[
|
|
4731
|
+
text(
|
|
4732
|
+
{
|
|
4733
|
+
as: "p",
|
|
4734
|
+
className: "text-base",
|
|
4735
|
+
style: textColorStyle("neutral-400")
|
|
4736
|
+
},
|
|
4737
|
+
bind("content.emptyMessage", { fallback: "No entries found." })
|
|
4738
|
+
)
|
|
4739
|
+
],
|
|
4740
|
+
when("data.entries", { not: true })
|
|
4741
|
+
)
|
|
4742
|
+
]),
|
|
4743
|
+
spacing: "comfortable"
|
|
4744
|
+
});
|
|
4745
|
+
var embedManifest = createBlockManifest({
|
|
4746
|
+
id: "block.embed",
|
|
4747
|
+
title: "Embed Content",
|
|
4748
|
+
category: "content",
|
|
4749
|
+
titleSource: "heading",
|
|
4750
|
+
additionalFields: embedFields,
|
|
4751
|
+
layout: embedLayout,
|
|
4752
|
+
description: "Embed content entries from any content type. Sites provide custom layouts via block overrides.",
|
|
4753
|
+
tags: ["embed", "content", "dynamic", "collection", "entries", "listing"],
|
|
4754
|
+
icon: "LayoutList",
|
|
4755
|
+
styleTokens: {
|
|
4756
|
+
spacing: "lg"
|
|
4757
|
+
}
|
|
4758
|
+
});
|
|
4759
|
+
var embedEntrySchema = zod.z.object({
|
|
4760
|
+
id: zod.z.string(),
|
|
4761
|
+
title: zod.z.string(),
|
|
4762
|
+
slug: zod.z.string().nullable().optional(),
|
|
4763
|
+
content: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
4764
|
+
publishedAt: zod.z.string().nullable().optional()
|
|
4765
|
+
});
|
|
4766
|
+
var embedBlockDefinition = {
|
|
4767
|
+
manifest: embedManifest,
|
|
4768
|
+
dataSchemas: {
|
|
4769
|
+
entries: zod.z.array(embedEntrySchema).optional()
|
|
4770
|
+
},
|
|
4771
|
+
dataLoaders: {
|
|
4772
|
+
entries: {
|
|
4773
|
+
endpoint: "listPublishedEntries",
|
|
4774
|
+
params: {
|
|
4775
|
+
type: { $bind: { from: "contentType" } },
|
|
4776
|
+
siteId: { $bind: { from: "$root.siteId" } },
|
|
4777
|
+
limit: { $bind: { from: "limit", fallback: "10" } },
|
|
4778
|
+
orderBy: { $bind: { from: "orderBy", fallback: "order" } },
|
|
4779
|
+
stage: { $bind: { from: "$root.previewStage", fallback: "published" } },
|
|
4780
|
+
// Manual mode entry IDs - loader should handle this
|
|
4781
|
+
entryIds: { $bind: { from: "entries" } },
|
|
4782
|
+
mode: { $bind: { from: "mode" } }
|
|
4783
|
+
},
|
|
4784
|
+
mode: "server"
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4787
|
+
};
|
|
4788
|
+
|
|
4789
|
+
// ../blocks/src/system/blocks/index.ts
|
|
4790
|
+
var systemBlockDefinitions = [
|
|
4791
|
+
heroBlockDefinition,
|
|
4792
|
+
bodyTextBlockDefinition,
|
|
4793
|
+
blogPostBlockDefinition,
|
|
4794
|
+
blogPlaceholderBlockDefinition,
|
|
4795
|
+
blogListingBlockDefinition,
|
|
4796
|
+
ctaFullBlockDefinition,
|
|
4797
|
+
formBlockDefinition,
|
|
4798
|
+
faqBlockDefinition,
|
|
4799
|
+
siteHeaderBlockDefinition,
|
|
4800
|
+
siteFooterBlockDefinition,
|
|
4801
|
+
testimonialsBlockDefinition,
|
|
4802
|
+
columnsBlockDefinition,
|
|
4803
|
+
appointmentBookingBlockDefinition,
|
|
4804
|
+
eventRegistrationBlockDefinition,
|
|
4805
|
+
// Event display blocks (3 specialized blocks)
|
|
4806
|
+
eventSpotlightBlockDefinition,
|
|
4807
|
+
eventListingBlockDefinition,
|
|
4808
|
+
eventCalendarBlockDefinition,
|
|
4809
|
+
// Content embedding
|
|
4810
|
+
embedBlockDefinition
|
|
4811
|
+
];
|
|
4812
|
+
var defaultsRegistered = false;
|
|
4813
|
+
function ensureSystemBlockDefinitionsRegistered() {
|
|
4814
|
+
if (defaultsRegistered) {
|
|
4815
|
+
return;
|
|
4816
|
+
}
|
|
4817
|
+
defaultsRegistered = true;
|
|
4818
|
+
for (const definition of systemBlockDefinitions) {
|
|
4819
|
+
registerBlockDefinition(definition);
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4822
|
+
|
|
4823
|
+
// ../blocks/src/system/registry.ts
|
|
4824
|
+
var REGISTRY_SYMBOL2 = Symbol.for("@riverbankcms/blocks/system/definitions");
|
|
4825
|
+
var globalScope2 = globalThis;
|
|
4826
|
+
if (!globalScope2[REGISTRY_SYMBOL2]) {
|
|
4827
|
+
globalScope2[REGISTRY_SYMBOL2] = /* @__PURE__ */ new Map();
|
|
4828
|
+
}
|
|
4829
|
+
var blockStore = globalScope2[REGISTRY_SYMBOL2];
|
|
4830
|
+
function ensureDefaults() {
|
|
4831
|
+
ensureSystemBlockDefinitionsRegistered();
|
|
4832
|
+
}
|
|
4833
|
+
function registerBlockDefinition(definition) {
|
|
4834
|
+
registerManifest(definition.manifest);
|
|
4835
|
+
blockStore.set(definition.manifest.name, definition);
|
|
4836
|
+
return definition;
|
|
4837
|
+
}
|
|
4838
|
+
function getBlockDefinition(name) {
|
|
4839
|
+
ensureDefaults();
|
|
4840
|
+
return blockStore.get(name);
|
|
4841
|
+
}
|
|
4842
|
+
|
|
4843
|
+
// src/data/prefetchBlockData.ts
|
|
13
4844
|
var SUPPORTED_LOADER_ENDPOINTS = [
|
|
14
4845
|
"listPublishedEntries",
|
|
15
4846
|
"getPublishedEntryPreview",
|
|
@@ -96,7 +4927,7 @@ var blockFieldOptionsSchema = zod.z.record(
|
|
|
96
4927
|
)
|
|
97
4928
|
).optional();
|
|
98
4929
|
var blockFieldExtensionSchema = zod.z.object({
|
|
99
|
-
fields:
|
|
4930
|
+
fields: fieldSchema.array().min(1, "At least one field is required")
|
|
100
4931
|
}).refine(
|
|
101
4932
|
(data) => {
|
|
102
4933
|
return data.fields.every((field) => {
|
|
@@ -118,7 +4949,7 @@ function validateFieldIdConflicts(blockFieldExtensions) {
|
|
|
118
4949
|
if (!blockFieldExtensions) return [];
|
|
119
4950
|
const conflicts = [];
|
|
120
4951
|
for (const [blockId, extension] of Object.entries(blockFieldExtensions)) {
|
|
121
|
-
const definition =
|
|
4952
|
+
const definition = getBlockDefinition(blockId);
|
|
122
4953
|
if (!definition) {
|
|
123
4954
|
conflicts.push({
|
|
124
4955
|
blockId,
|
|
@@ -128,9 +4959,9 @@ function validateFieldIdConflicts(blockFieldExtensions) {
|
|
|
128
4959
|
continue;
|
|
129
4960
|
}
|
|
130
4961
|
const existingFieldIds = /* @__PURE__ */ new Set();
|
|
131
|
-
const collectFieldIds = (
|
|
132
|
-
if (!
|
|
133
|
-
for (const field of
|
|
4962
|
+
const collectFieldIds = (fields4) => {
|
|
4963
|
+
if (!fields4) return;
|
|
4964
|
+
for (const field of fields4) {
|
|
134
4965
|
existingFieldIds.add(field.id);
|
|
135
4966
|
if (field.type === "group" || field.type === "modal") {
|
|
136
4967
|
collectFieldIds(field.schema?.fields);
|
|
@@ -164,11 +4995,11 @@ var sdkCustomBlockSchema = zod.z.object({
|
|
|
164
4995
|
title: zod.z.string().min(1, "Title is required"),
|
|
165
4996
|
titleSource: zod.z.string().optional(),
|
|
166
4997
|
description: zod.z.string().optional(),
|
|
167
|
-
category:
|
|
4998
|
+
category: blockCategoryEnum,
|
|
168
4999
|
icon: zod.z.string().optional(),
|
|
169
5000
|
tags: zod.z.array(zod.z.string()).optional(),
|
|
170
5001
|
// Reuse the exact field schema from @riverbankcms/blocks - all field types supported
|
|
171
|
-
fields:
|
|
5002
|
+
fields: fieldSchema.array().min(1, "Custom blocks must have at least one field"),
|
|
172
5003
|
// Data loaders for CMS endpoints
|
|
173
5004
|
dataLoaders: sdkDataLoadersSchema
|
|
174
5005
|
}).refine(
|