@bettercms-ai/next 0.5.2
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/README.md +223 -0
- package/dist/bettercms-snapshot.js +292 -0
- package/dist/bettercms-snapshot.js.map +1 -0
- package/dist/blocks.d.ts +36 -0
- package/dist/blocks.js +496 -0
- package/dist/blocks.js.map +1 -0
- package/dist/form.d.ts +28 -0
- package/dist/form.js +203 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +516 -0
- package/dist/index.js +583 -0
- package/dist/index.js.map +1 -0
- package/dist/live.d.ts +15 -0
- package/dist/live.js +27 -0
- package/dist/live.js.map +1 -0
- package/dist/search.d.ts +32 -0
- package/dist/search.js +111 -0
- package/dist/search.js.map +1 -0
- package/dist/visual-editing.d.ts +7 -0
- package/dist/visual-editing.js +35 -0
- package/dist/visual-editing.js.map +1 -0
- package/package.json +86 -0
package/dist/blocks.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { ContentBlock, DeliveryComponent } from '@betttercms/types';
|
|
3
|
+
import { DeliveryForm } from '@betttercms/sdk';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* <BcmsBlocks> — render a BetterCMS page's block_json natively in React.
|
|
7
|
+
*
|
|
8
|
+
* Pass the `blocks` from a page (delivery API `/content/:slug` → `entry.blocks`)
|
|
9
|
+
* plus the `forms` you read with `readForms()`. Every block type renders to plain,
|
|
10
|
+
* class-driven markup (the host site owns the styling); a `form` block resolves its
|
|
11
|
+
* `formId` against `forms` and renders `<BcmsForm>` inline exactly where the user
|
|
12
|
+
* placed it in the page builder. This is the turnkey "Webflow" path — forms show up
|
|
13
|
+
* wherever they were dropped on the page, with no manual wiring.
|
|
14
|
+
*
|
|
15
|
+
* Ships from its own "use client" entry (like <BcmsForm>) so it can be rendered from
|
|
16
|
+
* a Next App Router Server Component without breaking the RSC boundary.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface BcmsBlocksProps {
|
|
20
|
+
/** A page's blocks (delivery API `/content/:slug` → `entry.blocks`). */
|
|
21
|
+
blocks: ContentBlock[];
|
|
22
|
+
/** Forms available to `form` blocks — from `readForms().forms`. */
|
|
23
|
+
forms?: DeliveryForm[];
|
|
24
|
+
/** Reusable components available to `component` blocks — from `readComponents()`. */
|
|
25
|
+
components?: DeliveryComponent[];
|
|
26
|
+
/** Turnstile site key — forwarded to `<BcmsForm>` for form blocks. */
|
|
27
|
+
turnstileSiteKey?: string;
|
|
28
|
+
/** API origin forwarded to `<BcmsForm>`. Defaults to the SDK default. */
|
|
29
|
+
apiBase?: string;
|
|
30
|
+
/** Class for the wrapping <div>. */
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
/** Render a page's blocks. Unknown/empty blocks render nothing (fail-soft). */
|
|
34
|
+
declare function BcmsBlocks({ blocks, forms, components, turnstileSiteKey, apiBase, className, }: BcmsBlocksProps): ReactElement;
|
|
35
|
+
|
|
36
|
+
export { BcmsBlocks, type BcmsBlocksProps };
|
package/dist/blocks.js
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
// src/blocks.tsx
|
|
5
|
+
import { useState as useState2 } from "react";
|
|
6
|
+
|
|
7
|
+
// ../types/dist/index.js
|
|
8
|
+
var SPACE_PX = { none: "0", xs: "4px", sm: "8px", md: "16px", lg: "32px", xl: "64px" };
|
|
9
|
+
var FONT_SIZE_REM = { sm: "0.875rem", base: "1rem", lg: "1.25rem", xl: "1.5rem", "2xl": "2rem" };
|
|
10
|
+
var WEIGHT = { normal: 400, medium: 500, semibold: 600, bold: 700 };
|
|
11
|
+
var RADIUS_PX = { none: "0", sm: "4px", md: "8px", lg: "16px", full: "9999px" };
|
|
12
|
+
var COLOR_HEX = { white: "#ffffff", black: "#111111", muted: "#6b7280", mutedBg: "#f3f4f6", primary: "#111111", accent: "#4f46e5" };
|
|
13
|
+
function blockStyleToCss(style) {
|
|
14
|
+
if (!style || typeof style !== "object") return void 0;
|
|
15
|
+
const s = style;
|
|
16
|
+
const css = {};
|
|
17
|
+
if (s.paddingY && SPACE_PX[s.paddingY] != null) {
|
|
18
|
+
css.paddingTop = SPACE_PX[s.paddingY];
|
|
19
|
+
css.paddingBottom = SPACE_PX[s.paddingY];
|
|
20
|
+
}
|
|
21
|
+
if (s.paddingX && SPACE_PX[s.paddingX] != null) {
|
|
22
|
+
css.paddingLeft = SPACE_PX[s.paddingX];
|
|
23
|
+
css.paddingRight = SPACE_PX[s.paddingX];
|
|
24
|
+
}
|
|
25
|
+
if (s.marginTop && SPACE_PX[s.marginTop] != null) css.marginTop = SPACE_PX[s.marginTop];
|
|
26
|
+
if (s.marginBottom && SPACE_PX[s.marginBottom] != null) css.marginBottom = SPACE_PX[s.marginBottom];
|
|
27
|
+
if (s.background && COLOR_HEX[s.background]) css.background = COLOR_HEX[s.background];
|
|
28
|
+
if (s.textColor && COLOR_HEX[s.textColor]) css.color = COLOR_HEX[s.textColor];
|
|
29
|
+
if (s.align === "left" || s.align === "center" || s.align === "right") css.textAlign = s.align;
|
|
30
|
+
if (s.fontSize && FONT_SIZE_REM[s.fontSize]) css.fontSize = FONT_SIZE_REM[s.fontSize];
|
|
31
|
+
if (s.weight && WEIGHT[s.weight] != null) css.fontWeight = WEIGHT[s.weight];
|
|
32
|
+
if (s.radius && RADIUS_PX[s.radius] != null) css.borderRadius = RADIUS_PX[s.radius];
|
|
33
|
+
return Object.keys(css).length ? css : void 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ../ui/dist/resolve.js
|
|
37
|
+
function childArrays(block) {
|
|
38
|
+
const p = block.props ?? {};
|
|
39
|
+
switch (block.type) {
|
|
40
|
+
case "columns":
|
|
41
|
+
return Array.isArray(p.columns) ? p.columns : [];
|
|
42
|
+
case "section":
|
|
43
|
+
return Array.isArray(p.children) ? [p.children] : [];
|
|
44
|
+
case "slider":
|
|
45
|
+
return Array.isArray(p.slides) ? p.slides.map((s) => Array.isArray(s.children) ? s.children : []) : [];
|
|
46
|
+
case "tabs":
|
|
47
|
+
return Array.isArray(p.tabs) ? p.tabs.map((t) => Array.isArray(t.children) ? t.children : []) : [];
|
|
48
|
+
default:
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function findBlockById(blocks, id) {
|
|
53
|
+
for (const b of blocks) {
|
|
54
|
+
if (b?.id === id) return b;
|
|
55
|
+
for (const child of childArrays(b)) {
|
|
56
|
+
const found = findBlockById(child, id);
|
|
57
|
+
if (found) return found;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
function setPath(obj, path, value) {
|
|
63
|
+
const keys = path.split(".");
|
|
64
|
+
let cur = obj;
|
|
65
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
66
|
+
const k = keys[i];
|
|
67
|
+
if (typeof cur[k] !== "object" || cur[k] === null) cur[k] = {};
|
|
68
|
+
cur = cur[k];
|
|
69
|
+
}
|
|
70
|
+
cur[keys[keys.length - 1]] = value;
|
|
71
|
+
}
|
|
72
|
+
function applyOverrides(blockJson, propDefs, overrides) {
|
|
73
|
+
if (!Array.isArray(blockJson)) return [];
|
|
74
|
+
if (!propDefs.length || !overrides || Object.keys(overrides).length === 0) {
|
|
75
|
+
return blockJson;
|
|
76
|
+
}
|
|
77
|
+
const clone = structuredClone(blockJson);
|
|
78
|
+
for (const def of propDefs) {
|
|
79
|
+
if (!(def.key in overrides)) continue;
|
|
80
|
+
const target = findBlockById(clone, def.target.blockId);
|
|
81
|
+
if (target) setPath(target, def.target.path, overrides[def.key]);
|
|
82
|
+
}
|
|
83
|
+
return clone;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/form.tsx
|
|
87
|
+
import { useEffect, useMemo, useState } from "react";
|
|
88
|
+
import {
|
|
89
|
+
submitForm,
|
|
90
|
+
shouldShowField,
|
|
91
|
+
formInitialValues
|
|
92
|
+
} from "@bettercms-ai/sdk";
|
|
93
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
94
|
+
var TURNSTILE_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js";
|
|
95
|
+
var INPUT_TYPES = {
|
|
96
|
+
text: "text",
|
|
97
|
+
email: "email",
|
|
98
|
+
phone: "tel",
|
|
99
|
+
url: "url",
|
|
100
|
+
number: "number",
|
|
101
|
+
date: "date"
|
|
102
|
+
};
|
|
103
|
+
function readPrefill() {
|
|
104
|
+
if (typeof window === "undefined") return {};
|
|
105
|
+
const out = {};
|
|
106
|
+
new URLSearchParams(window.location.search).forEach((v, k) => {
|
|
107
|
+
out[k] = v;
|
|
108
|
+
});
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
function resetTurnstile() {
|
|
112
|
+
const t = globalThis.turnstile;
|
|
113
|
+
if (t) {
|
|
114
|
+
try {
|
|
115
|
+
t.reset();
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function BcmsForm({
|
|
121
|
+
form,
|
|
122
|
+
apiBase,
|
|
123
|
+
turnstileSiteKey,
|
|
124
|
+
onSubmitted,
|
|
125
|
+
className
|
|
126
|
+
}) {
|
|
127
|
+
const showTurnstile = Boolean(form.turnstileEnabled && turnstileSiteKey);
|
|
128
|
+
const [values, setValues] = useState(() => {
|
|
129
|
+
const init = formInitialValues(form, readPrefill());
|
|
130
|
+
if (form.honeypotField) init[form.honeypotField] = "";
|
|
131
|
+
return init;
|
|
132
|
+
});
|
|
133
|
+
const [status, setStatus] = useState({ state: "idle" });
|
|
134
|
+
const [fieldErrors, setFieldErrors] = useState({});
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
if (!showTurnstile || document.querySelector(`script[src="${TURNSTILE_SRC}"]`)) return;
|
|
137
|
+
const script = document.createElement("script");
|
|
138
|
+
script.src = TURNSTILE_SRC;
|
|
139
|
+
script.async = true;
|
|
140
|
+
script.defer = true;
|
|
141
|
+
document.head.appendChild(script);
|
|
142
|
+
}, [showTurnstile]);
|
|
143
|
+
const visibleFields = useMemo(
|
|
144
|
+
() => form.fields.filter((f) => f.type !== "hidden" && shouldShowField(f, values)),
|
|
145
|
+
[form.fields, values]
|
|
146
|
+
);
|
|
147
|
+
const setValue = (key, value) => setValues((prev) => ({ ...prev, [key]: value }));
|
|
148
|
+
async function handleSubmit(e) {
|
|
149
|
+
e.preventDefault();
|
|
150
|
+
setStatus({ state: "submitting" });
|
|
151
|
+
setFieldErrors({});
|
|
152
|
+
const tokenEl = e.currentTarget.querySelector(
|
|
153
|
+
'[name="cf-turnstile-response"]'
|
|
154
|
+
);
|
|
155
|
+
const turnstileToken = tokenEl?.value || void 0;
|
|
156
|
+
const data = {};
|
|
157
|
+
for (const field of form.fields) {
|
|
158
|
+
if (field.type === "hidden" || shouldShowField(field, values)) {
|
|
159
|
+
data[field.key] = values[field.key];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (form.honeypotField) data[form.honeypotField] = values[form.honeypotField] ?? "";
|
|
163
|
+
try {
|
|
164
|
+
const { id } = await submitForm({ formId: form.id, data, turnstileToken, baseUrl: apiBase });
|
|
165
|
+
onSubmitted?.(id);
|
|
166
|
+
if (form.redirectUrl) {
|
|
167
|
+
window.location.href = form.redirectUrl;
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const reset = formInitialValues(form);
|
|
171
|
+
if (form.honeypotField) reset[form.honeypotField] = "";
|
|
172
|
+
setValues(reset);
|
|
173
|
+
setStatus({
|
|
174
|
+
state: "success",
|
|
175
|
+
message: form.successMessage ?? "Thanks! Your submission was received."
|
|
176
|
+
});
|
|
177
|
+
resetTurnstile();
|
|
178
|
+
} catch (err) {
|
|
179
|
+
const e2 = err;
|
|
180
|
+
if (e2.fieldErrors) setFieldErrors(e2.fieldErrors);
|
|
181
|
+
setStatus({ state: "error", message: e2.message || "Something went wrong. Please try again." });
|
|
182
|
+
resetTurnstile();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return /* @__PURE__ */ jsxs("form", { className, onSubmit: handleSubmit, noValidate: true, children: [
|
|
186
|
+
visibleFields.map((field) => /* @__PURE__ */ jsx(
|
|
187
|
+
Field,
|
|
188
|
+
{
|
|
189
|
+
field,
|
|
190
|
+
value: values[field.key],
|
|
191
|
+
error: fieldErrors[field.key],
|
|
192
|
+
onChange: (v) => setValue(field.key, v)
|
|
193
|
+
},
|
|
194
|
+
field.key
|
|
195
|
+
)),
|
|
196
|
+
form.honeypotField ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { position: "absolute", left: "-9999px" }, children: /* @__PURE__ */ jsx(
|
|
197
|
+
"input",
|
|
198
|
+
{
|
|
199
|
+
type: "text",
|
|
200
|
+
name: form.honeypotField,
|
|
201
|
+
tabIndex: -1,
|
|
202
|
+
autoComplete: "off",
|
|
203
|
+
value: String(values[form.honeypotField] ?? ""),
|
|
204
|
+
onChange: (e) => setValue(form.honeypotField, e.target.value)
|
|
205
|
+
}
|
|
206
|
+
) }) : null,
|
|
207
|
+
showTurnstile ? /* @__PURE__ */ jsx("div", { className: "cf-turnstile", "data-sitekey": turnstileSiteKey }) : null,
|
|
208
|
+
/* @__PURE__ */ jsx("button", { type: "submit", disabled: status.state === "submitting", children: status.state === "submitting" ? "Submitting\u2026" : form.submitLabel ?? "Submit" }),
|
|
209
|
+
status.message ? /* @__PURE__ */ jsx(
|
|
210
|
+
"p",
|
|
211
|
+
{
|
|
212
|
+
className: `bcms-form-status bcms-form-status--${status.state}`,
|
|
213
|
+
role: "status",
|
|
214
|
+
"aria-live": "polite",
|
|
215
|
+
children: status.message
|
|
216
|
+
}
|
|
217
|
+
) : null
|
|
218
|
+
] });
|
|
219
|
+
}
|
|
220
|
+
function Field({ field, value, error, onChange }) {
|
|
221
|
+
const required = field.required;
|
|
222
|
+
const errorNode = error ? /* @__PURE__ */ jsx("span", { className: "bcms-field-error", role: "alert", children: error }) : null;
|
|
223
|
+
if (field.type === "checkbox" || field.type === "consent") {
|
|
224
|
+
return /* @__PURE__ */ jsxs("label", { className: "bcms-field bcms-field--checkbox", "data-field": field.key, children: [
|
|
225
|
+
/* @__PURE__ */ jsx(
|
|
226
|
+
"input",
|
|
227
|
+
{
|
|
228
|
+
type: "checkbox",
|
|
229
|
+
name: field.key,
|
|
230
|
+
required,
|
|
231
|
+
checked: Boolean(value),
|
|
232
|
+
onChange: (e) => onChange(e.target.checked)
|
|
233
|
+
}
|
|
234
|
+
),
|
|
235
|
+
" ",
|
|
236
|
+
field.label,
|
|
237
|
+
required ? " *" : "",
|
|
238
|
+
errorNode
|
|
239
|
+
] });
|
|
240
|
+
}
|
|
241
|
+
const str = String(value ?? "");
|
|
242
|
+
let control;
|
|
243
|
+
if (field.type === "textarea") {
|
|
244
|
+
control = /* @__PURE__ */ jsx(
|
|
245
|
+
"textarea",
|
|
246
|
+
{
|
|
247
|
+
name: field.key,
|
|
248
|
+
placeholder: field.placeholder,
|
|
249
|
+
required,
|
|
250
|
+
value: str,
|
|
251
|
+
onChange: (e) => onChange(e.target.value)
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
} else if (field.type === "select") {
|
|
255
|
+
control = /* @__PURE__ */ jsxs("select", { name: field.key, required, value: str, onChange: (e) => onChange(e.target.value), children: [
|
|
256
|
+
/* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Choose\u2026" }),
|
|
257
|
+
(field.options ?? []).map((opt) => /* @__PURE__ */ jsx("option", { value: opt, children: opt }, opt))
|
|
258
|
+
] });
|
|
259
|
+
} else {
|
|
260
|
+
control = /* @__PURE__ */ jsx(
|
|
261
|
+
"input",
|
|
262
|
+
{
|
|
263
|
+
type: INPUT_TYPES[field.type] ?? "text",
|
|
264
|
+
name: field.key,
|
|
265
|
+
placeholder: field.placeholder,
|
|
266
|
+
required,
|
|
267
|
+
value: str,
|
|
268
|
+
onChange: (e) => onChange(e.target.value)
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
return /* @__PURE__ */ jsxs("p", { className: "bcms-field", "data-field": field.key, children: [
|
|
273
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
274
|
+
field.label,
|
|
275
|
+
required ? " *" : ""
|
|
276
|
+
] }),
|
|
277
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
278
|
+
control,
|
|
279
|
+
errorNode
|
|
280
|
+
] });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/blocks.tsx
|
|
284
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
285
|
+
var MAX_COMPONENT_DEPTH = 10;
|
|
286
|
+
function BcmsBlocks({
|
|
287
|
+
blocks,
|
|
288
|
+
forms,
|
|
289
|
+
components,
|
|
290
|
+
turnstileSiteKey,
|
|
291
|
+
apiBase,
|
|
292
|
+
className
|
|
293
|
+
}) {
|
|
294
|
+
const formsById = new Map((forms ?? []).map((f) => [f.id, f]));
|
|
295
|
+
const componentsById = new Map((components ?? []).map((cmp) => [cmp.id, cmp]));
|
|
296
|
+
const ctx = { formsById, componentsById, turnstileSiteKey, apiBase, depth: 0, seen: /* @__PURE__ */ new Set() };
|
|
297
|
+
return /* @__PURE__ */ jsx2("div", { className, children: blocks.map((block) => renderBlock(block, ctx)) });
|
|
298
|
+
}
|
|
299
|
+
function renderBlock(block, ctx) {
|
|
300
|
+
const node = renderBlockInner(block, ctx);
|
|
301
|
+
const style = blockStyleToCss(block.style);
|
|
302
|
+
if (!style || node == null) return node;
|
|
303
|
+
return /* @__PURE__ */ jsx2("div", { style, children: node }, block.id);
|
|
304
|
+
}
|
|
305
|
+
function renderBlockInner(block, ctx) {
|
|
306
|
+
switch (block.type) {
|
|
307
|
+
case "heading":
|
|
308
|
+
return renderHeading(block);
|
|
309
|
+
case "text":
|
|
310
|
+
return renderText(block);
|
|
311
|
+
case "image":
|
|
312
|
+
return renderImage(block);
|
|
313
|
+
case "button":
|
|
314
|
+
return renderButton(block);
|
|
315
|
+
case "spacer":
|
|
316
|
+
return renderSpacer(block);
|
|
317
|
+
case "video":
|
|
318
|
+
return renderVideo(block);
|
|
319
|
+
case "columns":
|
|
320
|
+
return renderColumns(block, ctx);
|
|
321
|
+
case "form":
|
|
322
|
+
return renderForm(block, ctx);
|
|
323
|
+
case "section":
|
|
324
|
+
return renderSection(block, ctx);
|
|
325
|
+
case "navbar":
|
|
326
|
+
return renderNavbar(block);
|
|
327
|
+
case "footer":
|
|
328
|
+
return renderFooter(block);
|
|
329
|
+
case "slider":
|
|
330
|
+
return /* @__PURE__ */ jsx2(BcmsSlider, { block, ctx }, block.id);
|
|
331
|
+
case "tabs":
|
|
332
|
+
return /* @__PURE__ */ jsx2(BcmsTabs, { block, ctx }, block.id);
|
|
333
|
+
case "component":
|
|
334
|
+
return renderComponent(block, ctx);
|
|
335
|
+
default:
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function renderHeading({ id, props }) {
|
|
340
|
+
const Tag = `h${props.level ?? 2}`;
|
|
341
|
+
return /* @__PURE__ */ jsx2(Tag, { className: "bcms-block bcms-block--heading", children: props.text }, id);
|
|
342
|
+
}
|
|
343
|
+
function renderText({ id, props }) {
|
|
344
|
+
return /* @__PURE__ */ jsx2(
|
|
345
|
+
"div",
|
|
346
|
+
{
|
|
347
|
+
className: "bcms-block bcms-block--text",
|
|
348
|
+
dangerouslySetInnerHTML: { __html: props.html }
|
|
349
|
+
},
|
|
350
|
+
id
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
function renderImage({ id, props }) {
|
|
354
|
+
const img = /* @__PURE__ */ jsx2(
|
|
355
|
+
"img",
|
|
356
|
+
{
|
|
357
|
+
src: props.src,
|
|
358
|
+
alt: props.alt,
|
|
359
|
+
width: props.width,
|
|
360
|
+
height: props.height,
|
|
361
|
+
loading: "lazy"
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
if (!props.caption) {
|
|
365
|
+
return /* @__PURE__ */ jsx2("figure", { className: "bcms-block bcms-block--image", children: img }, id);
|
|
366
|
+
}
|
|
367
|
+
return /* @__PURE__ */ jsxs2("figure", { className: "bcms-block bcms-block--image", children: [
|
|
368
|
+
img,
|
|
369
|
+
/* @__PURE__ */ jsx2("figcaption", { children: props.caption })
|
|
370
|
+
] }, id);
|
|
371
|
+
}
|
|
372
|
+
function renderButton({ id, props }) {
|
|
373
|
+
return /* @__PURE__ */ jsx2(
|
|
374
|
+
"a",
|
|
375
|
+
{
|
|
376
|
+
href: props.href,
|
|
377
|
+
className: `bcms-block bcms-block--button bcms-button--${props.variant ?? "primary"}`,
|
|
378
|
+
children: props.text
|
|
379
|
+
},
|
|
380
|
+
id
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
function renderSpacer({ id, props }) {
|
|
384
|
+
return /* @__PURE__ */ jsx2(
|
|
385
|
+
"div",
|
|
386
|
+
{
|
|
387
|
+
className: "bcms-block bcms-block--spacer",
|
|
388
|
+
style: { height: props.height },
|
|
389
|
+
"aria-hidden": "true"
|
|
390
|
+
},
|
|
391
|
+
id
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
function renderVideo({ id, props }) {
|
|
395
|
+
return /* @__PURE__ */ jsx2(
|
|
396
|
+
"video",
|
|
397
|
+
{
|
|
398
|
+
className: "bcms-block bcms-block--video",
|
|
399
|
+
src: props.url,
|
|
400
|
+
poster: props.poster,
|
|
401
|
+
autoPlay: props.autoplay,
|
|
402
|
+
loop: props.loop,
|
|
403
|
+
controls: true
|
|
404
|
+
},
|
|
405
|
+
id
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
function renderColumns({ id, props }, ctx) {
|
|
409
|
+
return /* @__PURE__ */ jsx2(
|
|
410
|
+
"div",
|
|
411
|
+
{
|
|
412
|
+
className: "bcms-block bcms-block--columns",
|
|
413
|
+
style: {
|
|
414
|
+
display: "grid",
|
|
415
|
+
gridTemplateColumns: `repeat(${props.columns.length || 1}, 1fr)`,
|
|
416
|
+
gap: props.gap
|
|
417
|
+
},
|
|
418
|
+
children: props.columns.map((column, i) => /* @__PURE__ */ jsx2("div", { className: "bcms-column", children: column.map((child) => renderBlock(child, ctx)) }, i))
|
|
419
|
+
},
|
|
420
|
+
id
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
function renderForm({ id, props }, ctx) {
|
|
424
|
+
const form = ctx.formsById.get(props.formId);
|
|
425
|
+
if (!form) return null;
|
|
426
|
+
return /* @__PURE__ */ jsx2("div", { className: "bcms-block bcms-block--form", children: /* @__PURE__ */ jsx2(BcmsForm, { form, turnstileSiteKey: ctx.turnstileSiteKey, apiBase: ctx.apiBase }) }, id);
|
|
427
|
+
}
|
|
428
|
+
function renderSection({ id, props }, ctx) {
|
|
429
|
+
const inner = props.children.map((child) => renderBlock(child, ctx));
|
|
430
|
+
return /* @__PURE__ */ jsx2(
|
|
431
|
+
"section",
|
|
432
|
+
{
|
|
433
|
+
className: "bcms-block bcms-block--section",
|
|
434
|
+
style: { background: props.background, padding: props.paddingY != null ? `${props.paddingY}px 0` : void 0 },
|
|
435
|
+
children: props.maxWidth ? /* @__PURE__ */ jsx2("div", { style: { maxWidth: props.maxWidth, margin: "0 auto" }, children: inner }) : inner
|
|
436
|
+
},
|
|
437
|
+
id
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
function renderNavbar({ id, props }) {
|
|
441
|
+
return /* @__PURE__ */ jsxs2("nav", { className: "bcms-block bcms-block--navbar", children: [
|
|
442
|
+
props.logo?.src ? /* @__PURE__ */ jsx2("a", { className: "bcms-navbar-logo", href: props.logo.href ?? "/", children: /* @__PURE__ */ jsx2("img", { src: props.logo.src, alt: props.logo.alt }) }) : null,
|
|
443
|
+
/* @__PURE__ */ jsx2("div", { className: "bcms-navbar-links", children: props.links.map((l, i) => /* @__PURE__ */ jsx2("a", { href: l.href, children: l.label }, i)) }),
|
|
444
|
+
props.cta ? /* @__PURE__ */ jsx2("a", { className: "bcms-button bcms-button--primary", href: props.cta.href, children: props.cta.text }) : null
|
|
445
|
+
] }, id);
|
|
446
|
+
}
|
|
447
|
+
function renderFooter({ id, props }) {
|
|
448
|
+
return /* @__PURE__ */ jsxs2("footer", { className: "bcms-block bcms-block--footer", children: [
|
|
449
|
+
/* @__PURE__ */ jsx2("div", { className: "bcms-footer-cols", children: props.columns.map((col, i) => /* @__PURE__ */ jsxs2("div", { className: "bcms-footer-col", children: [
|
|
450
|
+
col.heading ? /* @__PURE__ */ jsx2("h4", { children: col.heading }) : null,
|
|
451
|
+
/* @__PURE__ */ jsx2("ul", { children: col.links.map((l, j) => /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2("a", { href: l.href, children: l.label }) }, j)) })
|
|
452
|
+
] }, i)) }),
|
|
453
|
+
props.copyright ? /* @__PURE__ */ jsx2("p", { className: "bcms-footer-copy", children: props.copyright }) : null
|
|
454
|
+
] }, id);
|
|
455
|
+
}
|
|
456
|
+
function BcmsSlider({ block, ctx }) {
|
|
457
|
+
const { slides } = block.props;
|
|
458
|
+
const [i, setI] = useState2(0);
|
|
459
|
+
const go = (n) => setI((n + slides.length) % (slides.length || 1));
|
|
460
|
+
return /* @__PURE__ */ jsxs2("div", { className: "bcms-block bcms-block--slider", style: { position: "relative" }, children: [
|
|
461
|
+
slides.map((s, idx) => /* @__PURE__ */ jsx2("div", { className: "bcms-slide", hidden: idx !== i, children: s.children.map((child) => renderBlock(child, ctx)) }, s.id)),
|
|
462
|
+
/* @__PURE__ */ jsx2("button", { type: "button", className: "bcms-slider-prev", "aria-label": "Previous", onClick: () => go(i - 1), children: "\u2039" }),
|
|
463
|
+
/* @__PURE__ */ jsx2("button", { type: "button", className: "bcms-slider-next", "aria-label": "Next", onClick: () => go(i + 1), children: "\u203A" })
|
|
464
|
+
] });
|
|
465
|
+
}
|
|
466
|
+
function BcmsTabs({ block, ctx }) {
|
|
467
|
+
const { tabs } = block.props;
|
|
468
|
+
const [active, setActive] = useState2(0);
|
|
469
|
+
return /* @__PURE__ */ jsxs2("div", { className: "bcms-block bcms-block--tabs", children: [
|
|
470
|
+
/* @__PURE__ */ jsx2("div", { className: "bcms-tablist", role: "tablist", children: tabs.map((t, idx) => /* @__PURE__ */ jsx2(
|
|
471
|
+
"button",
|
|
472
|
+
{
|
|
473
|
+
role: "tab",
|
|
474
|
+
type: "button",
|
|
475
|
+
className: "bcms-tab",
|
|
476
|
+
"aria-selected": idx === active,
|
|
477
|
+
onClick: () => setActive(idx),
|
|
478
|
+
children: t.label
|
|
479
|
+
},
|
|
480
|
+
t.id
|
|
481
|
+
)) }),
|
|
482
|
+
tabs.map((t, idx) => /* @__PURE__ */ jsx2("div", { role: "tabpanel", className: "bcms-tabpanel", hidden: idx !== active, children: t.children.map((child) => renderBlock(child, ctx)) }, t.id))
|
|
483
|
+
] });
|
|
484
|
+
}
|
|
485
|
+
function renderComponent({ id, props }, ctx) {
|
|
486
|
+
const def = ctx.componentsById.get(props.componentId);
|
|
487
|
+
if (!def) return null;
|
|
488
|
+
if (ctx.depth >= MAX_COMPONENT_DEPTH || ctx.seen.has(def.id)) return null;
|
|
489
|
+
const resolved = applyOverrides(def.blockJson, def.props ?? [], props.overrides ?? {});
|
|
490
|
+
const childCtx = { ...ctx, depth: ctx.depth + 1, seen: /* @__PURE__ */ new Set([...ctx.seen, def.id]) };
|
|
491
|
+
return /* @__PURE__ */ jsx2("div", { className: "bcms-block bcms-block--component", children: resolved.map((child) => renderBlock(child, childCtx)) }, id);
|
|
492
|
+
}
|
|
493
|
+
export {
|
|
494
|
+
BcmsBlocks
|
|
495
|
+
};
|
|
496
|
+
//# sourceMappingURL=blocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/blocks.tsx","../../types/src/block.ts","../../types/src/blockStyle.ts","../../ui/src/resolve.ts","../src/form.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * <BcmsBlocks> — render a BetterCMS page's block_json natively in React.\n *\n * Pass the `blocks` from a page (delivery API `/content/:slug` → `entry.blocks`)\n * plus the `forms` you read with `readForms()`. Every block type renders to plain,\n * class-driven markup (the host site owns the styling); a `form` block resolves its\n * `formId` against `forms` and renders `<BcmsForm>` inline exactly where the user\n * placed it in the page builder. This is the turnkey \"Webflow\" path — forms show up\n * wherever they were dropped on the page, with no manual wiring.\n *\n * Ships from its own \"use client\" entry (like <BcmsForm>) so it can be rendered from\n * a Next App Router Server Component without breaking the RSC boundary.\n */\n\nimport { useState, type CSSProperties, type ReactElement, type ReactNode } from \"react\";\nimport { blockStyleToCss } from \"@betttercms/types\";\nimport type {\n ContentBlock,\n HeadingBlock,\n TextBlock,\n ImageBlock,\n ButtonBlock,\n SpacerBlock,\n VideoBlock,\n ColumnsBlock,\n FormBlock,\n SectionBlock,\n NavbarBlock,\n FooterBlock,\n SliderBlock,\n TabsBlock,\n ComponentBlock,\n DeliveryComponent,\n} from \"@betttercms/types\";\nimport type { DeliveryForm } from \"@betttercms/sdk\";\nimport { applyOverrides } from \"@betttercms/ui/resolve\";\nimport { BcmsForm } from \"./form.js\";\n\nconst MAX_COMPONENT_DEPTH = 10;\n\nexport interface BcmsBlocksProps {\n /** A page's blocks (delivery API `/content/:slug` → `entry.blocks`). */\n blocks: ContentBlock[];\n /** Forms available to `form` blocks — from `readForms().forms`. */\n forms?: DeliveryForm[];\n /** Reusable components available to `component` blocks — from `readComponents()`. */\n components?: DeliveryComponent[];\n /** Turnstile site key — forwarded to `<BcmsForm>` for form blocks. */\n turnstileSiteKey?: string;\n /** API origin forwarded to `<BcmsForm>`. Defaults to the SDK default. */\n apiBase?: string;\n /** Class for the wrapping <div>. */\n className?: string;\n}\n\n/** Render a page's blocks. Unknown/empty blocks render nothing (fail-soft). */\nexport function BcmsBlocks({\n blocks,\n forms,\n components,\n turnstileSiteKey,\n apiBase,\n className,\n}: BcmsBlocksProps): ReactElement {\n const formsById = new Map((forms ?? []).map((f) => [f.id, f]));\n const componentsById = new Map((components ?? []).map((cmp) => [cmp.id, cmp]));\n const ctx: RenderCtx = { formsById, componentsById, turnstileSiteKey, apiBase, depth: 0, seen: new Set() };\n return <div className={className}>{blocks.map((block) => renderBlock(block, ctx))}</div>;\n}\n\ninterface RenderCtx {\n formsById: Map<string, DeliveryForm>;\n componentsById: Map<string, DeliveryComponent>;\n turnstileSiteKey?: string;\n apiBase?: string;\n depth: number;\n seen: Set<string>;\n}\n\nfunction renderBlock(block: ContentBlock, ctx: RenderCtx): ReactNode {\n const node = renderBlockInner(block, ctx);\n const style = blockStyleToCss(block.style);\n if (!style || node == null) return node;\n // FLO-264: honor the block's bounded style tokens in the headless render too, so\n // @betttercms/next output matches the builder canvas + hosted site.\n return (\n <div key={block.id} style={style as CSSProperties}>\n {node}\n </div>\n );\n}\n\nfunction renderBlockInner(block: ContentBlock, ctx: RenderCtx): ReactNode {\n switch (block.type) {\n case \"heading\":\n return renderHeading(block);\n case \"text\":\n return renderText(block);\n case \"image\":\n return renderImage(block);\n case \"button\":\n return renderButton(block);\n case \"spacer\":\n return renderSpacer(block);\n case \"video\":\n return renderVideo(block);\n case \"columns\":\n return renderColumns(block, ctx);\n case \"form\":\n return renderForm(block, ctx);\n case \"section\":\n return renderSection(block, ctx);\n case \"navbar\":\n return renderNavbar(block);\n case \"footer\":\n return renderFooter(block);\n case \"slider\":\n return <BcmsSlider key={block.id} block={block} ctx={ctx} />;\n case \"tabs\":\n return <BcmsTabs key={block.id} block={block} ctx={ctx} />;\n case \"component\":\n return renderComponent(block, ctx);\n default:\n return null;\n }\n}\n\nfunction renderHeading({ id, props }: HeadingBlock): ReactNode {\n const Tag = `h${props.level ?? 2}` as \"h1\" | \"h2\" | \"h3\";\n return (\n <Tag key={id} className=\"bcms-block bcms-block--heading\">\n {props.text}\n </Tag>\n );\n}\n\nfunction renderText({ id, props }: TextBlock): ReactNode {\n // `html` is sanitized server-side before storage.\n return (\n <div\n key={id}\n className=\"bcms-block bcms-block--text\"\n dangerouslySetInnerHTML={{ __html: props.html }}\n />\n );\n}\n\nfunction renderImage({ id, props }: ImageBlock): ReactNode {\n const img = (\n <img\n src={props.src}\n alt={props.alt}\n width={props.width}\n height={props.height}\n loading=\"lazy\"\n />\n );\n if (!props.caption) {\n return (\n <figure key={id} className=\"bcms-block bcms-block--image\">\n {img}\n </figure>\n );\n }\n return (\n <figure key={id} className=\"bcms-block bcms-block--image\">\n {img}\n <figcaption>{props.caption}</figcaption>\n </figure>\n );\n}\n\nfunction renderButton({ id, props }: ButtonBlock): ReactNode {\n return (\n <a\n key={id}\n href={props.href}\n className={`bcms-block bcms-block--button bcms-button--${props.variant ?? \"primary\"}`}\n >\n {props.text}\n </a>\n );\n}\n\nfunction renderSpacer({ id, props }: SpacerBlock): ReactNode {\n return (\n <div\n key={id}\n className=\"bcms-block bcms-block--spacer\"\n style={{ height: props.height }}\n aria-hidden=\"true\"\n />\n );\n}\n\nfunction renderVideo({ id, props }: VideoBlock): ReactNode {\n return (\n <video\n key={id}\n className=\"bcms-block bcms-block--video\"\n src={props.url}\n poster={props.poster}\n autoPlay={props.autoplay}\n loop={props.loop}\n controls\n />\n );\n}\n\nfunction renderColumns({ id, props }: ColumnsBlock, ctx: RenderCtx): ReactNode {\n return (\n <div\n key={id}\n className=\"bcms-block bcms-block--columns\"\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${props.columns.length || 1}, 1fr)`,\n gap: props.gap,\n }}\n >\n {props.columns.map((column, i) => (\n <div key={i} className=\"bcms-column\">\n {column.map((child) => renderBlock(child, ctx))}\n </div>\n ))}\n </div>\n );\n}\n\nfunction renderForm({ id, props }: FormBlock, ctx: RenderCtx): ReactNode {\n const form = ctx.formsById.get(props.formId);\n if (!form) return null; // form deleted or not delivered — render nothing\n return (\n <div key={id} className=\"bcms-block bcms-block--form\">\n <BcmsForm form={form} turnstileSiteKey={ctx.turnstileSiteKey} apiBase={ctx.apiBase} />\n </div>\n );\n}\n\nfunction renderSection({ id, props }: SectionBlock, ctx: RenderCtx): ReactNode {\n const inner = props.children.map((child) => renderBlock(child, ctx));\n return (\n <section\n key={id}\n className=\"bcms-block bcms-block--section\"\n style={{ background: props.background, padding: props.paddingY != null ? `${props.paddingY}px 0` : undefined }}\n >\n {props.maxWidth ? <div style={{ maxWidth: props.maxWidth, margin: \"0 auto\" }}>{inner}</div> : inner}\n </section>\n );\n}\n\nfunction renderNavbar({ id, props }: NavbarBlock): ReactNode {\n return (\n <nav key={id} className=\"bcms-block bcms-block--navbar\">\n {props.logo?.src ? (\n <a className=\"bcms-navbar-logo\" href={props.logo.href ?? \"/\"}>\n <img src={props.logo.src} alt={props.logo.alt} />\n </a>\n ) : null}\n <div className=\"bcms-navbar-links\">\n {props.links.map((l, i) => (\n <a key={i} href={l.href}>\n {l.label}\n </a>\n ))}\n </div>\n {props.cta ? (\n <a className=\"bcms-button bcms-button--primary\" href={props.cta.href}>\n {props.cta.text}\n </a>\n ) : null}\n </nav>\n );\n}\n\nfunction renderFooter({ id, props }: FooterBlock): ReactNode {\n return (\n <footer key={id} className=\"bcms-block bcms-block--footer\">\n <div className=\"bcms-footer-cols\">\n {props.columns.map((col, i) => (\n <div key={i} className=\"bcms-footer-col\">\n {col.heading ? <h4>{col.heading}</h4> : null}\n <ul>\n {col.links.map((l, j) => (\n <li key={j}>\n <a href={l.href}>{l.label}</a>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n {props.copyright ? <p className=\"bcms-footer-copy\">{props.copyright}</p> : null}\n </footer>\n );\n}\n\nfunction BcmsSlider({ block, ctx }: { block: SliderBlock; ctx: RenderCtx }): ReactElement {\n const { slides } = block.props;\n const [i, setI] = useState(0);\n const go = (n: number) => setI((n + slides.length) % (slides.length || 1));\n return (\n <div className=\"bcms-block bcms-block--slider\" style={{ position: \"relative\" }}>\n {slides.map((s, idx) => (\n <div key={s.id} className=\"bcms-slide\" hidden={idx !== i}>\n {s.children.map((child) => renderBlock(child, ctx))}\n </div>\n ))}\n <button type=\"button\" className=\"bcms-slider-prev\" aria-label=\"Previous\" onClick={() => go(i - 1)}>\n ‹\n </button>\n <button type=\"button\" className=\"bcms-slider-next\" aria-label=\"Next\" onClick={() => go(i + 1)}>\n ›\n </button>\n </div>\n );\n}\n\nfunction BcmsTabs({ block, ctx }: { block: TabsBlock; ctx: RenderCtx }): ReactElement {\n const { tabs } = block.props;\n const [active, setActive] = useState(0);\n return (\n <div className=\"bcms-block bcms-block--tabs\">\n <div className=\"bcms-tablist\" role=\"tablist\">\n {tabs.map((t, idx) => (\n <button\n key={t.id}\n role=\"tab\"\n type=\"button\"\n className=\"bcms-tab\"\n aria-selected={idx === active}\n onClick={() => setActive(idx)}\n >\n {t.label}\n </button>\n ))}\n </div>\n {tabs.map((t, idx) => (\n <div key={t.id} role=\"tabpanel\" className=\"bcms-tabpanel\" hidden={idx !== active}>\n {t.children.map((child) => renderBlock(child, ctx))}\n </div>\n ))}\n </div>\n );\n}\n\nfunction renderComponent({ id, props }: ComponentBlock, ctx: RenderCtx): ReactNode {\n const def = ctx.componentsById.get(props.componentId);\n if (!def) return null; // not delivered — fail-soft\n if (ctx.depth >= MAX_COMPONENT_DEPTH || ctx.seen.has(def.id)) return null; // cycle/depth guard\n const resolved = applyOverrides<ContentBlock>(def.blockJson, def.props ?? [], props.overrides ?? {});\n const childCtx: RenderCtx = { ...ctx, depth: ctx.depth + 1, seen: new Set([...ctx.seen, def.id]) };\n return (\n <div key={id} className=\"bcms-block bcms-block--component\">\n {resolved.map((child) => renderBlock(child, childCtx))}\n </div>\n );\n}\n\n","/**\n * ContentBlock — a discriminated union of all CMS block types.\n * Each block has a `type` discriminator and a `props` object\n * whose shape matches the block type.\n */\n\n// ── Block style (FLO-264) ──────────────────────────────────────────────────────\n// A uniform, BOUNDED set of design tokens any block may carry (sibling to `props`).\n// Tokens map to fixed CSS values in the renderer (`styleTokensToCss`) so styling\n// reaches the live site WITHOUT arbitrary CSS. Additive + fully optional.\n\nexport type SpaceToken = \"none\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\nexport type ColorToken = \"none\" | \"white\" | \"black\" | \"muted\" | \"mutedBg\" | \"primary\" | \"accent\";\nexport type AlignToken = \"left\" | \"center\" | \"right\";\nexport type FontSizeToken = \"sm\" | \"base\" | \"lg\" | \"xl\" | \"2xl\";\nexport type WeightToken = \"normal\" | \"medium\" | \"semibold\" | \"bold\";\nexport type RadiusToken = \"none\" | \"sm\" | \"md\" | \"lg\" | \"full\";\n\nexport interface BlockStyle {\n paddingY?: SpaceToken;\n paddingX?: SpaceToken;\n marginTop?: SpaceToken;\n marginBottom?: SpaceToken;\n background?: ColorToken;\n textColor?: ColorToken;\n align?: AlignToken;\n fontSize?: FontSizeToken;\n weight?: WeightToken;\n radius?: RadiusToken;\n}\n\n// ── Heading ──────────────────────────────────────────────────────────────────\n\nexport interface HeadingProps {\n text: string;\n level?: 1 | 2 | 3;\n}\n\nexport interface HeadingBlock {\n type: \"heading\";\n id: string;\n style?: BlockStyle;\n props: HeadingProps;\n}\n\n// ── Text ─────────────────────────────────────────────────────────────────────\n\nexport interface TextProps {\n html: string;\n}\n\nexport interface TextBlock {\n type: \"text\";\n id: string;\n style?: BlockStyle;\n props: TextProps;\n}\n\n// ── Image ────────────────────────────────────────────────────────────────────\n\nexport interface ImageProps {\n src: string;\n alt: string;\n width?: number;\n height?: number;\n caption?: string;\n}\n\nexport interface ImageBlock {\n type: \"image\";\n id: string;\n style?: BlockStyle;\n props: ImageProps;\n}\n\n// ── Button ───────────────────────────────────────────────────────────────────\n\nexport interface ButtonProps {\n text: string;\n href: string;\n variant?: \"primary\" | \"secondary\";\n}\n\nexport interface ButtonBlock {\n type: \"button\";\n id: string;\n style?: BlockStyle;\n props: ButtonProps;\n}\n\n// ── Spacer ───────────────────────────────────────────────────────────────────\n\nexport interface SpacerProps {\n height: number; // in px\n}\n\nexport interface SpacerBlock {\n type: \"spacer\";\n id: string;\n style?: BlockStyle;\n props: SpacerProps;\n}\n\n// ── Columns ───────────────────────────────────────────────────────────────────\n\nexport interface ColumnsProps {\n columns: ContentBlock[][]; // array of column slots, each slot is an array of blocks\n gap?: number; // in px\n}\n\nexport interface ColumnsBlock {\n type: \"columns\";\n id: string;\n style?: BlockStyle;\n props: ColumnsProps;\n}\n\n// ── Video ────────────────────────────────────────────────────────────────────\n\nexport interface VideoProps {\n url: string;\n poster?: string;\n autoplay?: boolean;\n loop?: boolean;\n}\n\nexport interface VideoBlock {\n type: \"video\";\n id: string;\n style?: BlockStyle;\n props: VideoProps;\n}\n\n// ── Form ─────────────────────────────────────────────────────────────────────\n\nexport interface FormProps {\n formId: string; // references forms.id (a nanoid). Resolved against delivered forms at render time.\n}\n\nexport interface FormBlock {\n type: \"form\";\n id: string;\n style?: BlockStyle;\n props: FormProps;\n}\n\n// ── Section (recursive container) ──────────────────────────────────────────────\n\nexport interface SectionProps {\n children: ContentBlock[];\n background?: string;\n paddingY?: number; // in px\n maxWidth?: number; // in px\n}\n\nexport interface SectionBlock {\n type: \"section\";\n id: string;\n style?: BlockStyle;\n props: SectionProps;\n}\n\n// ── Navbar ─────────────────────────────────────────────────────────────────────\n\nexport interface NavLink {\n label: string;\n href: string;\n}\n\nexport interface NavbarProps {\n logo?: { src: string; alt: string; href?: string };\n links: NavLink[];\n cta?: { text: string; href: string };\n}\n\nexport interface NavbarBlock {\n type: \"navbar\";\n id: string;\n style?: BlockStyle;\n props: NavbarProps;\n}\n\n// ── Footer ─────────────────────────────────────────────────────────────────────\n\nexport interface FooterColumn {\n heading?: string;\n links: NavLink[];\n}\n\nexport interface FooterProps {\n columns: FooterColumn[];\n copyright?: string;\n}\n\nexport interface FooterBlock {\n type: \"footer\";\n id: string;\n style?: BlockStyle;\n props: FooterProps;\n}\n\n// ── Slider / Carousel (recursive container, client-hydrated) ────────────────────\n\nexport interface SlideItem {\n id: string;\n children: ContentBlock[];\n}\n\nexport interface SliderProps {\n slides: SlideItem[];\n autoplay?: boolean;\n interval?: number; // in ms\n loop?: boolean;\n}\n\nexport interface SliderBlock {\n type: \"slider\";\n id: string;\n style?: BlockStyle;\n props: SliderProps;\n}\n\n// ── Tabs (recursive container, client-hydrated) ─────────────────────────────────\n\nexport interface TabItem {\n id: string;\n label: string;\n children: ContentBlock[];\n}\n\nexport interface TabsProps {\n tabs: TabItem[];\n}\n\nexport interface TabsBlock {\n type: \"tabs\";\n id: string;\n style?: BlockStyle;\n props: TabsProps;\n}\n\n// ── Component instance (Webflow-style reusable symbol) ──────────────────────────\n// References a reusable component by id. `overrides` maps a component prop key\n// (declared in the component's `props[]` allowlist) to a value, applied to the\n// component's block tree at render time. Resolved against delivered components.\n\nexport interface ComponentProps {\n componentId: string;\n overrides?: Record<string, unknown>;\n}\n\nexport interface ComponentBlock {\n type: \"component\";\n id: string;\n style?: BlockStyle;\n props: ComponentProps;\n}\n\n// ── Union ─────────────────────────────────────────────────────────────────────\n\nexport type ContentBlock =\n | HeadingBlock\n | TextBlock\n | ImageBlock\n | ButtonBlock\n | SpacerBlock\n | ColumnsBlock\n | VideoBlock\n | FormBlock\n | SectionBlock\n | NavbarBlock\n | FooterBlock\n | SliderBlock\n | TabsBlock\n | ComponentBlock;\n\n/** Literal union of all block type discriminators. */\nexport type BlockType = ContentBlock[\"type\"];\n\n/**\n * Type guard — narrows an unknown block to `ContentBlock`.\n *\n * @example\n * const block = maybeBlock as unknown;\n * if (isBlock(block)) {\n * // block is ContentBlock here\n * }\n */\nexport function isBlock(value: unknown): value is ContentBlock {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"type\" in value &&\n typeof (value as Record<string, unknown>)[\"type\"] === \"string\"\n );\n}\n\n/**\n * Extract the `type` discriminator from a block, or `null` if not a block.\n *\n * @example\n * const block = { type: \"heading\", id: \"1\", props: { text: \"Hi\" } };\n * getBlockType(block); // \"heading\"\n * getBlockType({ not: \"a block\" }); // null\n */\nexport function getBlockType(value: unknown): BlockType | null {\n if (isBlock(value)) return value.type;\n return null;\n}\n","/**\n * FLO-264 — bounded block-style tokens → CSS. Framework-agnostic (returns a plain\n * declaration object, no React types) so every renderer shares ONE mapping: the\n * headless @betttercms/next adapter, @betttercms/ui, and (string form) the hosted\n * render-page.ts. Unknown tokens are dropped — closed set, no arbitrary CSS.\n */\nimport type { BlockStyle } from \"./block.js\";\n\nconst SPACE_PX: Record<string, string> = { none: \"0\", xs: \"4px\", sm: \"8px\", md: \"16px\", lg: \"32px\", xl: \"64px\" };\nconst FONT_SIZE_REM: Record<string, string> = { sm: \"0.875rem\", base: \"1rem\", lg: \"1.25rem\", xl: \"1.5rem\", \"2xl\": \"2rem\" };\nconst WEIGHT: Record<string, number> = { normal: 400, medium: 500, semibold: 600, bold: 700 };\nconst RADIUS_PX: Record<string, string> = { none: \"0\", sm: \"4px\", md: \"8px\", lg: \"16px\", full: \"9999px\" };\nconst COLOR_HEX: Record<string, string> = { white: \"#ffffff\", black: \"#111111\", muted: \"#6b7280\", mutedBg: \"#f3f4f6\", primary: \"#111111\", accent: \"#4f46e5\" };\n\n/** A CSS declaration object (key → value) usable as a React `style` prop, or undefined when empty. */\nexport function blockStyleToCss(style: BlockStyle | undefined): Record<string, string | number> | undefined {\n if (!style || typeof style !== \"object\") return undefined;\n const s = style as Record<string, string>;\n const css: Record<string, string | number> = {};\n if (s.paddingY && SPACE_PX[s.paddingY] != null) { css.paddingTop = SPACE_PX[s.paddingY]!; css.paddingBottom = SPACE_PX[s.paddingY]!; }\n if (s.paddingX && SPACE_PX[s.paddingX] != null) { css.paddingLeft = SPACE_PX[s.paddingX]!; css.paddingRight = SPACE_PX[s.paddingX]!; }\n if (s.marginTop && SPACE_PX[s.marginTop] != null) css.marginTop = SPACE_PX[s.marginTop]!;\n if (s.marginBottom && SPACE_PX[s.marginBottom] != null) css.marginBottom = SPACE_PX[s.marginBottom]!;\n if (s.background && COLOR_HEX[s.background]) css.background = COLOR_HEX[s.background]!;\n if (s.textColor && COLOR_HEX[s.textColor]) css.color = COLOR_HEX[s.textColor]!;\n if (s.align === \"left\" || s.align === \"center\" || s.align === \"right\") css.textAlign = s.align;\n if (s.fontSize && FONT_SIZE_REM[s.fontSize]) css.fontSize = FONT_SIZE_REM[s.fontSize]!;\n if (s.weight && WEIGHT[s.weight] != null) css.fontWeight = WEIGHT[s.weight]!;\n if (s.radius && RADIUS_PX[s.radius] != null) css.borderRadius = RADIUS_PX[s.radius]!;\n return Object.keys(css).length ? css : undefined;\n}\n","/**\n * Pure component-instance resolver — clone a component's blockJson and write each\n * declared override at its target (blockId + dotted path), honoring the props[]\n * allowlist. The single source of truth shared by the dashboard canvas preview, the\n * backend live HTML renderer, and @bettercms-ai/next's SSG output, so all three resolve\n * instances identically. React-free and exported on its own subpath (@bettercms-ai/ui/\n * resolve) so the Hono server can import it without pulling in the block components.\n */\n\n/** Minimal block shape the resolver needs — every real block type is structurally wider.\n * `props` is `unknown` (not `Record<string, unknown>`) so typed block unions like\n * @bettercms-ai/types' ContentBlock — whose `props` are specific interfaces without an\n * index signature — still satisfy the constraint. */\nexport type ResolverBlock = { type: string; id?: string; props?: unknown };\n\n/** Minimal prop-def shape — the real ComponentPropDef is structurally wider. */\nexport type ResolverPropDef = { key: string; target: { blockId: string; path: string } };\n\n/** The child block arrays a container holds, one entry per nested slot (all container types). */\nexport function childArrays<B extends ResolverBlock>(block: B): B[][] {\n const p = (block.props ?? {}) as Record<string, unknown>;\n switch (block.type) {\n case \"columns\":\n return Array.isArray(p.columns) ? (p.columns as B[][]) : [];\n case \"section\":\n return Array.isArray(p.children) ? [p.children as B[]] : [];\n case \"slider\":\n return Array.isArray(p.slides)\n ? (p.slides as { children?: B[] }[]).map((s) => (Array.isArray(s.children) ? s.children : []))\n : [];\n case \"tabs\":\n return Array.isArray(p.tabs)\n ? (p.tabs as { children?: B[] }[]).map((t) => (Array.isArray(t.children) ? t.children : []))\n : [];\n default:\n return [];\n }\n}\n\n/** Find a block by id anywhere in a tree (descends every container). */\nexport function findBlockById<B extends ResolverBlock>(blocks: B[], id: string): B | null {\n for (const b of blocks) {\n if (b?.id === id) return b;\n for (const child of childArrays(b)) {\n const found = findBlockById(child, id);\n if (found) return found;\n }\n }\n return null;\n}\n\n/** Write `value` at a dotted path (e.g. \"props.text\"), creating objects as needed. */\nexport function setPath(obj: Record<string, unknown>, path: string, value: unknown): void {\n const keys = path.split(\".\");\n let cur: Record<string, unknown> = obj;\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i]!;\n if (typeof cur[k] !== \"object\" || cur[k] === null) cur[k] = {};\n cur = cur[k] as Record<string, unknown>;\n }\n cur[keys[keys.length - 1]!] = value;\n}\n\n/**\n * Apply an instance's `overrides` onto a clone of a component's blockJson, honoring the\n * declared `props[]` allowlist (key → target block + path). Returns the original array\n * untouched (referentially stable) when there's nothing to override; returns [] for a\n * non-array input.\n */\nexport function applyOverrides<B extends ResolverBlock>(\n blockJson: unknown,\n propDefs: ResolverPropDef[],\n overrides: Record<string, unknown> | undefined,\n): B[] {\n if (!Array.isArray(blockJson)) return [];\n if (!propDefs.length || !overrides || Object.keys(overrides).length === 0) {\n return blockJson as B[];\n }\n const clone = structuredClone(blockJson) as B[];\n for (const def of propDefs) {\n if (!(def.key in overrides)) continue;\n const target = findBlockById(clone, def.target.blockId);\n if (target) setPath(target as unknown as Record<string, unknown>, def.target.path, overrides[def.key]);\n }\n return clone;\n}\n","\"use client\";\n\n/**\n * <BcmsForm> — render a BetterCMS form natively in React, the headless way.\n *\n * Pass a form straight from your content snapshot (`readSnapshot().forms[n]`) or\n * the delivery API. It renders real inputs (no iframe), handles conditional\n * `showIf` fields, URL-query prefill, the honeypot, an optional Turnstile widget,\n * and submits via `@bettercms-ai/sdk`'s `submitForm`. All visible markup is\n * unstyled and class-driven, so the host site owns the look.\n */\n\nimport { useEffect, useMemo, useState, type FormEvent, type ReactElement } from \"react\";\nimport {\n submitForm,\n shouldShowField,\n formInitialValues,\n type DeliveryForm,\n type DeliveryFormField,\n type FormValues,\n type FormSubmitError,\n} from \"@bettercms-ai/sdk\";\n\nconst TURNSTILE_SRC = \"https://challenges.cloudflare.com/turnstile/v0/api.js\";\n\n// Field type → native <input type>. Other types render their own element.\nconst INPUT_TYPES: Partial<Record<DeliveryFormField[\"type\"], string>> = {\n text: \"text\",\n email: \"email\",\n phone: \"tel\",\n url: \"url\",\n number: \"number\",\n date: \"date\",\n};\n\nexport interface BcmsFormProps {\n /** A form from your content snapshot or the delivery API. */\n form: DeliveryForm;\n /** API origin. Defaults to the SDK default (https://api.bettercms.ai). */\n apiBase?: string;\n /** Turnstile site key — needed to render the widget when `form.turnstileEnabled`. */\n turnstileSiteKey?: string;\n /** Called with the new submission id after a successful submit (before any redirect). */\n onSubmitted?: (submissionId: string) => void;\n /** Class for the <form> element; field wrappers/status use `bcms-*` classes. */\n className?: string;\n}\n\ntype Status = { state: \"idle\" | \"submitting\" | \"error\" | \"success\"; message?: string };\n\nfunction readPrefill(): Record<string, string> {\n if (typeof window === \"undefined\") return {};\n const out: Record<string, string> = {};\n new URLSearchParams(window.location.search).forEach((v, k) => {\n out[k] = v;\n });\n return out;\n}\n\nfunction resetTurnstile(): void {\n const t = (globalThis as { turnstile?: { reset: () => void } }).turnstile;\n if (t) {\n try {\n t.reset();\n } catch {\n /* widget not mounted yet */\n }\n }\n}\n\nexport function BcmsForm({\n form,\n apiBase,\n turnstileSiteKey,\n onSubmitted,\n className,\n}: BcmsFormProps): ReactElement {\n const showTurnstile = Boolean(form.turnstileEnabled && turnstileSiteKey);\n const [values, setValues] = useState<FormValues>(() => {\n const init = formInitialValues(form, readPrefill());\n if (form.honeypotField) init[form.honeypotField] = \"\";\n return init;\n });\n const [status, setStatus] = useState<Status>({ state: \"idle\" });\n const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});\n\n // Cloudflare auto-renders any `.cf-turnstile` element once its script is present.\n useEffect(() => {\n if (!showTurnstile || document.querySelector(`script[src=\"${TURNSTILE_SRC}\"]`)) return;\n const script = document.createElement(\"script\");\n script.src = TURNSTILE_SRC;\n script.async = true;\n script.defer = true;\n document.head.appendChild(script);\n }, [showTurnstile]);\n\n const visibleFields = useMemo(\n () => form.fields.filter((f) => f.type !== \"hidden\" && shouldShowField(f, values)),\n [form.fields, values],\n );\n\n const setValue = (key: string, value: string | boolean) =>\n setValues((prev) => ({ ...prev, [key]: value }));\n\n async function handleSubmit(e: FormEvent<HTMLFormElement>): Promise<void> {\n e.preventDefault();\n setStatus({ state: \"submitting\" });\n setFieldErrors({});\n\n const tokenEl = e.currentTarget.querySelector<HTMLInputElement>(\n '[name=\"cf-turnstile-response\"]',\n );\n const turnstileToken = tokenEl?.value || undefined;\n\n // Drop conditionally-hidden fields so a hidden required field can't block submit;\n // keep hidden-type fields and the honeypot (the API classifies bots on it).\n const data: FormValues = {};\n for (const field of form.fields) {\n if (field.type === \"hidden\" || shouldShowField(field, values)) {\n data[field.key] = values[field.key];\n }\n }\n if (form.honeypotField) data[form.honeypotField] = values[form.honeypotField] ?? \"\";\n\n try {\n const { id } = await submitForm({ formId: form.id, data, turnstileToken, baseUrl: apiBase });\n onSubmitted?.(id);\n if (form.redirectUrl) {\n window.location.href = form.redirectUrl;\n return;\n }\n const reset = formInitialValues(form);\n if (form.honeypotField) reset[form.honeypotField] = \"\";\n setValues(reset);\n setStatus({\n state: \"success\",\n message: form.successMessage ?? \"Thanks! Your submission was received.\",\n });\n resetTurnstile();\n } catch (err) {\n const e2 = err as FormSubmitError;\n if (e2.fieldErrors) setFieldErrors(e2.fieldErrors);\n setStatus({ state: \"error\", message: e2.message || \"Something went wrong. Please try again.\" });\n resetTurnstile();\n }\n }\n\n return (\n <form className={className} onSubmit={handleSubmit} noValidate>\n {visibleFields.map((field) => (\n <Field\n key={field.key}\n field={field}\n value={values[field.key]}\n error={fieldErrors[field.key]}\n onChange={(v) => setValue(field.key, v)}\n />\n ))}\n\n {form.honeypotField ? (\n <span aria-hidden=\"true\" style={{ position: \"absolute\", left: \"-9999px\" }}>\n <input\n type=\"text\"\n name={form.honeypotField}\n tabIndex={-1}\n autoComplete=\"off\"\n value={String(values[form.honeypotField] ?? \"\")}\n onChange={(e) => setValue(form.honeypotField as string, e.target.value)}\n />\n </span>\n ) : null}\n\n {showTurnstile ? <div className=\"cf-turnstile\" data-sitekey={turnstileSiteKey} /> : null}\n\n <button type=\"submit\" disabled={status.state === \"submitting\"}>\n {status.state === \"submitting\" ? \"Submitting…\" : form.submitLabel ?? \"Submit\"}\n </button>\n\n {status.message ? (\n <p\n className={`bcms-form-status bcms-form-status--${status.state}`}\n role=\"status\"\n aria-live=\"polite\"\n >\n {status.message}\n </p>\n ) : null}\n </form>\n );\n}\n\ninterface FieldProps {\n field: DeliveryFormField;\n value: string | boolean;\n error?: string;\n onChange: (value: string | boolean) => void;\n}\n\nfunction Field({ field, value, error, onChange }: FieldProps): ReactElement {\n const required = field.required;\n const errorNode = error ? (\n <span className=\"bcms-field-error\" role=\"alert\">\n {error}\n </span>\n ) : null;\n\n if (field.type === \"checkbox\" || field.type === \"consent\") {\n return (\n <label className=\"bcms-field bcms-field--checkbox\" data-field={field.key}>\n <input\n type=\"checkbox\"\n name={field.key}\n required={required}\n checked={Boolean(value)}\n onChange={(e) => onChange(e.target.checked)}\n />{\" \"}\n {field.label}\n {required ? \" *\" : \"\"}\n {errorNode}\n </label>\n );\n }\n\n const str = String(value ?? \"\");\n let control: ReactElement;\n if (field.type === \"textarea\") {\n control = (\n <textarea\n name={field.key}\n placeholder={field.placeholder}\n required={required}\n value={str}\n onChange={(e) => onChange(e.target.value)}\n />\n );\n } else if (field.type === \"select\") {\n control = (\n <select name={field.key} required={required} value={str} onChange={(e) => onChange(e.target.value)}>\n <option value=\"\" disabled>\n Choose…\n </option>\n {(field.options ?? []).map((opt) => (\n <option key={opt} value={opt}>\n {opt}\n </option>\n ))}\n </select>\n );\n } else {\n control = (\n <input\n type={INPUT_TYPES[field.type] ?? \"text\"}\n name={field.key}\n placeholder={field.placeholder}\n required={required}\n value={str}\n onChange={(e) => onChange(e.target.value)}\n />\n );\n }\n\n return (\n <p className=\"bcms-field\" data-field={field.key}>\n <label>\n {field.label}\n {required ? \" *\" : \"\"}\n </label>\n <br />\n {control}\n {errorNode}\n </p>\n );\n}\n"],"mappings":"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","names":["useState","jsx","jsxs","useState"]}
|