@bison-lab/payload-blocks 3.2.0 → 3.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/README.md +48 -0
- package/dist/index.d.mts +40 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +227 -24
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +61 -2
- package/dist/react.d.mts.map +1 -1
- package/dist/react.mjs +142 -69
- package/dist/react.mjs.map +1 -1
- package/dist/rich-text.d.mts +3 -1
- package/dist/rich-text.d.mts.map +1 -1
- package/dist/rich-text.mjs +12 -6
- package/dist/rich-text.mjs.map +1 -1
- package/dist/seam-BDFZlV3F.mjs +54 -0
- package/dist/seam-BDFZlV3F.mjs.map +1 -0
- package/dist/{types-CJAvoZMk.d.mts → types-D9rg0BoW.d.mts} +38 -4
- package/dist/types-D9rg0BoW.d.mts.map +1 -0
- package/package.json +2 -2
- package/dist/cx-BKHSv3xT.mjs +0 -17
- package/dist/cx-BKHSv3xT.mjs.map +0 -1
- package/dist/types-CJAvoZMk.d.mts.map +0 -1
package/dist/react.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { a as overlapsSeam, i as SEAM_PULL_UP, n as OVERLAP_BELOW_CLEARANCE, o as seamClasses, r as SEAM_PULL_DOWN, s as cx, t as OVERLAP_ABOVE_CLEARANCE } from "./seam-BDFZlV3F.mjs";
|
|
3
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
-
import { Button, FAQColumnsBlock, JsonLd, MegaMenuPanel, NapBlock, ProcessStepsBlock, ShowcasePanelsBlock, TestimonialMasonry, megaMenuToFloatingNavItems } from "@bison-lab/ui";
|
|
4
|
+
import { Button, FAQColumnsBlock, JsonLd, MegaMenuPanel, NapBlock, ProcessStepsBlock, ShowcasePanelsBlock, StatsBandBlock, TestimonialMasonry, megaMenuToFloatingNavItems } from "@bison-lab/ui";
|
|
5
5
|
//#region src/image.tsx
|
|
6
6
|
/**
|
|
7
7
|
* The fallback when no `imageComponent` is supplied: a plain `<img>`. Correct
|
|
@@ -86,15 +86,18 @@ function runIndexes(blocks) {
|
|
|
86
86
|
* Renders a page's blocks in order through the registry. A Server Component:
|
|
87
87
|
* it only maps and looks up, and each renderer decides its own nature.
|
|
88
88
|
*/
|
|
89
|
-
function RenderBlocks({ blocks, registry, containerClassName = DEFAULT_CONTAINER, imageComponent = DefaultBlockImage, linkComponent = DefaultBlockLink }) {
|
|
89
|
+
function RenderBlocks({ blocks, registry, containerClassName = DEFAULT_CONTAINER, imageComponent = DefaultBlockImage, linkComponent = DefaultBlockLink, floats = overlapsSeam }) {
|
|
90
90
|
const rows = renderable(blocks, registry);
|
|
91
91
|
const runs = runIndexes(rows.map((row) => row.block));
|
|
92
|
+
const floating = rows.map((row) => floats(row.block));
|
|
92
93
|
return /* @__PURE__ */ jsx(Fragment, { children: rows.map(({ block, Renderer }, index) => /* @__PURE__ */ jsx(Renderer, {
|
|
93
94
|
block,
|
|
94
95
|
index,
|
|
95
96
|
prevType: index > 0 ? rows[index - 1].block.blockType : void 0,
|
|
96
97
|
runIndex: runs[index],
|
|
97
98
|
isLast: index === rows.length - 1,
|
|
99
|
+
overlapAbove: index > 0 && floating[index - 1],
|
|
100
|
+
overlapBelow: index < rows.length - 1 && floating[index + 1],
|
|
98
101
|
containerClassName,
|
|
99
102
|
imageComponent,
|
|
100
103
|
linkComponent
|
|
@@ -146,12 +149,15 @@ const TONES = {
|
|
|
146
149
|
* render at all, not to be the final answer. A site overrides this one registry
|
|
147
150
|
* entry with its own and keeps every other block.
|
|
148
151
|
*/
|
|
149
|
-
function HeroBlockRenderer({ block, index, containerClassName, imageComponent: Image, linkComponent: Link }) {
|
|
152
|
+
function HeroBlockRenderer({ block, index, overlapAbove, overlapBelow, containerClassName, imageComponent: Image, linkComponent: Link }) {
|
|
150
153
|
const image = resolveMedia(block.image);
|
|
151
154
|
const links = (block.links ?? []).filter((link) => link.href && link.label);
|
|
152
155
|
const tone = TONES[block.tone ?? "sweep"];
|
|
153
156
|
return /* @__PURE__ */ jsxs("section", {
|
|
154
|
-
className: cx("relative isolate overflow-hidden", tone
|
|
157
|
+
className: cx("relative isolate overflow-hidden", tone, seamClasses({
|
|
158
|
+
overlapAbove,
|
|
159
|
+
overlapBelow
|
|
160
|
+
})),
|
|
155
161
|
children: [image ? /* @__PURE__ */ jsx("div", {
|
|
156
162
|
className: "absolute inset-0 -z-10 opacity-25",
|
|
157
163
|
children: /* @__PURE__ */ jsx(Image, {
|
|
@@ -200,7 +206,7 @@ function HeroBlockRenderer({ block, index, containerClassName, imageComponent: I
|
|
|
200
206
|
}
|
|
201
207
|
//#endregion
|
|
202
208
|
//#region src/blocks/showcase-panels/component.tsx
|
|
203
|
-
function ShowcasePanelsBlockRenderer({ block, containerClassName, imageComponent: Image, linkComponent }) {
|
|
209
|
+
function ShowcasePanelsBlockRenderer({ block, overlapAbove, overlapBelow, containerClassName, imageComponent: Image, linkComponent }) {
|
|
204
210
|
const dims = [];
|
|
205
211
|
const items = [];
|
|
206
212
|
for (const [i, row] of (block.items ?? []).entries()) {
|
|
@@ -221,27 +227,33 @@ function ShowcasePanelsBlockRenderer({ block, containerClassName, imageComponent
|
|
|
221
227
|
}
|
|
222
228
|
if (items.length === 0) return null;
|
|
223
229
|
return /* @__PURE__ */ jsx("section", {
|
|
224
|
-
className:
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
230
|
+
className: seamClasses({
|
|
231
|
+
overlapAbove,
|
|
232
|
+
overlapBelow
|
|
233
|
+
}),
|
|
234
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
235
|
+
className: cx(containerClassName, "py-16 lg:py-24"),
|
|
236
|
+
children: /* @__PURE__ */ jsx(ShowcasePanelsBlock, {
|
|
237
|
+
items,
|
|
238
|
+
spineVariant: block.spineVariant ?? "numbered",
|
|
239
|
+
defaultActiveIndex: Math.min(Math.max(block.defaultActiveIndex ?? 0, 0), items.length - 1),
|
|
240
|
+
...block.watermark ? { watermark: block.watermark } : {},
|
|
241
|
+
renderLink: renderLinkWith(linkComponent),
|
|
242
|
+
renderImage: (item, i) => /* @__PURE__ */ jsx(Image, {
|
|
243
|
+
src: item.image.src,
|
|
244
|
+
alt: item.image.alt ?? "",
|
|
245
|
+
width: dims[i]?.width,
|
|
246
|
+
height: dims[i]?.height,
|
|
247
|
+
className: "absolute inset-0 h-full w-full object-cover",
|
|
248
|
+
sizes: "(min-width: 1024px) 50vw, 100vw"
|
|
249
|
+
})
|
|
238
250
|
})
|
|
239
251
|
})
|
|
240
252
|
});
|
|
241
253
|
}
|
|
242
254
|
//#endregion
|
|
243
255
|
//#region src/blocks/process-steps/component.tsx
|
|
244
|
-
function ProcessStepsBlockRenderer({ block, containerClassName, imageComponent: Image }) {
|
|
256
|
+
function ProcessStepsBlockRenderer({ block, overlapAbove, overlapBelow, containerClassName, imageComponent: Image }) {
|
|
245
257
|
const dims = [];
|
|
246
258
|
const items = [];
|
|
247
259
|
for (const [i, row] of (block.items ?? []).entries()) {
|
|
@@ -260,27 +272,33 @@ function ProcessStepsBlockRenderer({ block, containerClassName, imageComponent:
|
|
|
260
272
|
}
|
|
261
273
|
if (items.length === 0) return null;
|
|
262
274
|
return /* @__PURE__ */ jsx("section", {
|
|
263
|
-
className:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
275
|
+
className: seamClasses({
|
|
276
|
+
overlapAbove,
|
|
277
|
+
overlapBelow
|
|
278
|
+
}),
|
|
279
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
280
|
+
className: cx(containerClassName, "py-16 lg:py-24"),
|
|
281
|
+
children: /* @__PURE__ */ jsx(ProcessStepsBlock, {
|
|
282
|
+
items,
|
|
283
|
+
autoAdvance: block.autoAdvance ?? true,
|
|
284
|
+
autoAdvanceDuration: block.autoAdvanceDuration ?? 6e3,
|
|
285
|
+
pauseOnHover: block.pauseOnHover ?? true,
|
|
286
|
+
defaultActiveIndex: Math.min(Math.max(block.defaultActiveIndex ?? 0, 0), items.length - 1),
|
|
287
|
+
renderImage: (item, i) => /* @__PURE__ */ jsx(Image, {
|
|
288
|
+
src: item.image.src,
|
|
289
|
+
alt: item.image.alt ?? "",
|
|
290
|
+
width: dims[i]?.width,
|
|
291
|
+
height: dims[i]?.height,
|
|
292
|
+
className: "size-full object-cover",
|
|
293
|
+
sizes: "(min-width: 768px) 66vw, 100vw"
|
|
294
|
+
})
|
|
277
295
|
})
|
|
278
296
|
})
|
|
279
297
|
});
|
|
280
298
|
}
|
|
281
299
|
//#endregion
|
|
282
300
|
//#region src/blocks/faq-columns/component.tsx
|
|
283
|
-
function FaqColumnsBlockRenderer({ block, containerClassName, linkComponent }) {
|
|
301
|
+
function FaqColumnsBlockRenderer({ block, overlapAbove, overlapBelow, containerClassName, linkComponent }) {
|
|
284
302
|
const items = (block.items ?? []).filter((row) => row.question && row.answer).map((row, i) => ({
|
|
285
303
|
id: row.id ?? String(i),
|
|
286
304
|
question: row.question,
|
|
@@ -292,31 +310,37 @@ function FaqColumnsBlockRenderer({ block, containerClassName, linkComponent }) {
|
|
|
292
310
|
if (items.length === 0) return null;
|
|
293
311
|
const cta = block.cta;
|
|
294
312
|
const hasCta = Boolean(cta?.href && cta?.linkText);
|
|
295
|
-
return /* @__PURE__ */ jsx("section", {
|
|
296
|
-
className:
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
313
|
+
return /* @__PURE__ */ jsx("section", {
|
|
314
|
+
className: seamClasses({
|
|
315
|
+
overlapAbove,
|
|
316
|
+
overlapBelow
|
|
317
|
+
}),
|
|
318
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
319
|
+
className: cx(containerClassName, "py-16 lg:py-24"),
|
|
320
|
+
children: /* @__PURE__ */ jsx(FAQColumnsBlock, {
|
|
321
|
+
className: "py-0 md:py-0",
|
|
322
|
+
containerClassName: "max-w-none px-0 sm:px-0",
|
|
323
|
+
items,
|
|
324
|
+
title: block.title ?? "",
|
|
325
|
+
...block.eyebrow ? { eyebrow: block.eyebrow } : {},
|
|
326
|
+
...block.description ? { description: block.description } : {},
|
|
327
|
+
sticky: block.sticky ?? true,
|
|
328
|
+
type: block.type ?? "single",
|
|
329
|
+
collapsible: block.collapsible ?? true,
|
|
330
|
+
renderLink: renderLinkWith(linkComponent),
|
|
331
|
+
...hasCta && cta ? { cta: {
|
|
332
|
+
title: cta.title ?? "",
|
|
333
|
+
linkText: cta.linkText,
|
|
334
|
+
href: cta.href,
|
|
335
|
+
newTab: cta.newTab ?? false
|
|
336
|
+
} } : {}
|
|
337
|
+
})
|
|
314
338
|
})
|
|
315
|
-
})
|
|
339
|
+
});
|
|
316
340
|
}
|
|
317
341
|
//#endregion
|
|
318
342
|
//#region src/blocks/testimonial-masonry/component.tsx
|
|
319
|
-
function TestimonialMasonryBlockRenderer({ block, containerClassName, linkComponent }) {
|
|
343
|
+
function TestimonialMasonryBlockRenderer({ block, overlapAbove, overlapBelow, containerClassName, linkComponent }) {
|
|
320
344
|
const items = (block.items ?? []).filter((row) => row.content && row.author?.name).map((row, i) => {
|
|
321
345
|
const avatar = resolveMedia(row.author?.avatar);
|
|
322
346
|
return {
|
|
@@ -333,7 +357,10 @@ function TestimonialMasonryBlockRenderer({ block, containerClassName, linkCompon
|
|
|
333
357
|
const link = block.link;
|
|
334
358
|
const hasLink = Boolean(link?.href && link?.label);
|
|
335
359
|
return /* @__PURE__ */ jsx("section", {
|
|
336
|
-
className: "bg-accent",
|
|
360
|
+
className: cx("bg-accent", seamClasses({
|
|
361
|
+
overlapAbove,
|
|
362
|
+
overlapBelow
|
|
363
|
+
})),
|
|
337
364
|
children: /* @__PURE__ */ jsx("div", {
|
|
338
365
|
className: cx(containerClassName, "py-16 lg:py-24"),
|
|
339
366
|
children: /* @__PURE__ */ jsx(TestimonialMasonry, {
|
|
@@ -357,7 +384,7 @@ function TestimonialMasonryBlockRenderer({ block, containerClassName, linkCompon
|
|
|
357
384
|
}
|
|
358
385
|
//#endregion
|
|
359
386
|
//#region src/blocks/nap/component.tsx
|
|
360
|
-
function NapBlockRenderer({ block, containerClassName, linkComponent }) {
|
|
387
|
+
function NapBlockRenderer({ block, overlapAbove, overlapBelow, containerClassName, linkComponent }) {
|
|
361
388
|
const address = block.address;
|
|
362
389
|
const departments = (block.departments ?? []).filter((row) => row.name && row.phoneE164).map((row, i) => ({
|
|
363
390
|
id: row.id ?? String(i),
|
|
@@ -382,15 +409,61 @@ function NapBlockRenderer({ block, containerClassName, linkComponent }) {
|
|
|
382
409
|
departments,
|
|
383
410
|
...block.url ? { url: block.url } : {}
|
|
384
411
|
};
|
|
385
|
-
return /* @__PURE__ */ jsx("section", {
|
|
386
|
-
className:
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
412
|
+
return /* @__PURE__ */ jsx("section", {
|
|
413
|
+
className: seamClasses({
|
|
414
|
+
overlapAbove,
|
|
415
|
+
overlapBelow
|
|
416
|
+
}),
|
|
417
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
418
|
+
className: cx(containerClassName, "py-16 lg:py-24"),
|
|
419
|
+
children: [/* @__PURE__ */ jsx(NapBlock, {
|
|
420
|
+
record,
|
|
421
|
+
showName: block.showName ?? true,
|
|
422
|
+
headingLevel: block.headingLevel ?? "h2",
|
|
423
|
+
renderLink: renderLinkWith(linkComponent)
|
|
424
|
+
}), block.emitJsonLd === false ? null : /* @__PURE__ */ jsx(JsonLd, { data: record })]
|
|
425
|
+
})
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region src/blocks/stats-band/component.tsx
|
|
430
|
+
/** The select only offers 2–6, but a site can edit the config; fall back to the design's four. */
|
|
431
|
+
function columnsFrom(value) {
|
|
432
|
+
const n = Number(value ?? 4);
|
|
433
|
+
return Number.isInteger(n) && n >= 2 && n <= 6 ? n : 4;
|
|
434
|
+
}
|
|
435
|
+
function StatsBandBlockRenderer({ block, index, isLast, overlapAbove, overlapBelow, containerClassName, linkComponent }) {
|
|
436
|
+
const items = (block.items ?? []).flatMap((row, i) => row.value ? [{
|
|
437
|
+
id: row.id ?? String(i),
|
|
438
|
+
value: row.value,
|
|
439
|
+
label: row.label ?? "",
|
|
440
|
+
...row.href ? { href: row.href } : {},
|
|
441
|
+
...row.wide ? { wide: true } : {}
|
|
442
|
+
}] : []);
|
|
443
|
+
if (items.length < 2) return null;
|
|
444
|
+
const floats = Boolean(block.overlap);
|
|
445
|
+
const overAbove = floats && index > 0 && !overlapAbove;
|
|
446
|
+
const overBelow = floats && !isLast && !overlapBelow;
|
|
447
|
+
return /* @__PURE__ */ jsx("section", {
|
|
448
|
+
className: cx("relative", floats && "z-10", seamClasses({
|
|
449
|
+
overlapAbove,
|
|
450
|
+
overlapBelow
|
|
451
|
+
})),
|
|
452
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
453
|
+
className: cx(containerClassName, overAbove ? SEAM_PULL_UP : "pt-16 lg:pt-24", overBelow ? SEAM_PULL_DOWN : "pb-16 lg:pb-24"),
|
|
454
|
+
children: /* @__PURE__ */ jsx(StatsBandBlock, {
|
|
455
|
+
items,
|
|
456
|
+
columns: columnsFrom(block.columns),
|
|
457
|
+
tone: block.tone ?? "card",
|
|
458
|
+
align: block.align ?? "start",
|
|
459
|
+
eyebrow: !floats && block.eyebrow || void 0,
|
|
460
|
+
title: !floats && block.title || void 0,
|
|
461
|
+
description: !floats && block.description || void 0,
|
|
462
|
+
containerClassName: "max-w-none px-0 sm:px-0",
|
|
463
|
+
renderLink: renderLinkWith(linkComponent)
|
|
464
|
+
})
|
|
465
|
+
})
|
|
466
|
+
});
|
|
394
467
|
}
|
|
395
468
|
//#endregion
|
|
396
469
|
//#region src/blocks/mega-menu/rem-width.ts
|
|
@@ -521,6 +594,6 @@ function headerItemsFromBlocks(blocks, { variants, icons, isActive, linkComponen
|
|
|
521
594
|
});
|
|
522
595
|
}
|
|
523
596
|
//#endregion
|
|
524
|
-
export { DEFAULT_CONTAINER, DefaultBlockImage, DefaultBlockLink, FaqColumnsBlockRenderer, HeroBlockRenderer, MegaMenuBlockRenderer, NapBlockRenderer, ProcessStepsBlockRenderer, RenderBlocks, ShowcasePanelsBlockRenderer, TestimonialMasonryBlockRenderer, headerItemsFromBlocks, megaMenuPanelFromRow, newTabAttributes, renderLinkWith };
|
|
597
|
+
export { DEFAULT_CONTAINER, DefaultBlockImage, DefaultBlockLink, FaqColumnsBlockRenderer, HeroBlockRenderer, MegaMenuBlockRenderer, NapBlockRenderer, OVERLAP_ABOVE_CLEARANCE, OVERLAP_BELOW_CLEARANCE, ProcessStepsBlockRenderer, RenderBlocks, SEAM_PULL_DOWN, SEAM_PULL_UP, ShowcasePanelsBlockRenderer, StatsBandBlockRenderer, TestimonialMasonryBlockRenderer, headerItemsFromBlocks, megaMenuPanelFromRow, newTabAttributes, overlapsSeam, renderLinkWith, seamClasses };
|
|
525
598
|
|
|
526
599
|
//# sourceMappingURL=react.mjs.map
|
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.mjs","names":[],"sources":["../src/image.tsx","../src/link.tsx","../src/render-blocks.tsx","../src/media.ts","../src/blocks/hero/component.tsx","../src/blocks/showcase-panels/component.tsx","../src/blocks/process-steps/component.tsx","../src/blocks/faq-columns/component.tsx","../src/blocks/testimonial-masonry/component.tsx","../src/blocks/nap/component.tsx","../src/blocks/mega-menu/rem-width.ts","../src/blocks/mega-menu/component.tsx","../src/blocks/mega-menu/header.tsx"],"sourcesContent":["import type { ComponentType } from \"react\";\n\n/**\n * The image injection point.\n *\n * The package deliberately does not depend on `next` — a Payload site is not\n * necessarily a Next.js site, and `next/image` needs per-site configuration\n * (`remotePatterns` for the media route) that a package cannot supply. So\n * renderers never construct an image element themselves; they render the\n * component they were handed. A Next site writes one adapter around\n * `next/image` and every block gets optimisation from it.\n *\n * This is a *component type*, not a render function, on purpose: a Server\n * Component may pass a client component reference across the boundary, but not\n * a closure. `imageComponent` therefore survives the trip from a server page\n * into these client renderers, where a `renderImage` callback would not.\n */\n\nexport interface BlockImageProps {\n src: string;\n /** Always a string — see `resolveMedia`. Empty means decorative. */\n alt: string;\n width?: number;\n height?: number;\n className?: string;\n /** Passed through to `next/image`'s `sizes`; ignored by the default. */\n sizes?: string;\n /** Set on the one image above the fold, typically the hero's. */\n priority?: boolean;\n}\n\nexport type BlockImageComponent = ComponentType<BlockImageProps>;\n\n/**\n * The fallback when no `imageComponent` is supplied: a plain `<img>`. Correct\n * everywhere and optimised nowhere, which is the right default for a package\n * that cannot know what framework it landed in.\n */\nexport function DefaultBlockImage({\n src,\n alt,\n width,\n height,\n className,\n sizes,\n priority,\n}: BlockImageProps) {\n return (\n <img\n src={src}\n alt={alt}\n width={width}\n height={height}\n className={className}\n sizes={sizes}\n loading={priority ? \"eager\" : \"lazy\"}\n decoding=\"async\"\n />\n );\n}\n","import type { AnchorHTMLAttributes, ComponentType, ReactNode } from \"react\";\nimport type { RenderLink } from \"@bison-lab/ui\";\n\n/**\n * The link injection point, the same shape as `BlockImageComponent`.\n *\n * A renderer never writes `<a>` directly. A site hands in one link component\n * and every CMS link routes through it — Next's `Link`, so navigation stays\n * client-side. It is a *component type*, not a render function, for the same\n * reason the image seam is: a Server Component can pass a client component\n * reference across the boundary, but not a closure.\n *\n * `newTab` is the flag from `linkFields()`; `target` and `rel` are always\n * already expanded from it by the time the component is called, so an adapter\n * that only spreads the anchor attributes onto its router link is still safe.\n * The one thing an adapter must do is drop `newTab` before spreading, or React\n * warns about an unknown DOM attribute — see the README's `next/link` adapter.\n */\nexport interface BlockLinkProps\n extends AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string;\n children: ReactNode;\n newTab?: boolean;\n className?: string;\n}\n\nexport type BlockLinkComponent = ComponentType<BlockLinkProps>;\n\n/** `target` and `rel` together, or neither: a bare `target` is a tabnabbing hole. */\nexport function newTabAttributes(\n newTab: boolean | null | undefined,\n): Pick<AnchorHTMLAttributes<HTMLAnchorElement>, \"target\" | \"rel\"> {\n return newTab ? { target: \"_blank\", rel: \"noopener noreferrer\" } : {};\n}\n\n/**\n * The fallback when no `linkComponent` is supplied: a plain `<a>`. Correct\n * everywhere and client-side nowhere, which is the right default for a package\n * that cannot know what router it landed in.\n */\nexport function DefaultBlockLink({\n href,\n newTab,\n children,\n ...rest\n}: BlockLinkProps) {\n return (\n <a href={href} {...rest} {...newTabAttributes(newTab)}>\n {children}\n </a>\n );\n}\nDefaultBlockLink.displayName = \"DefaultBlockLink\";\n\n/**\n * Adapts the seam to the `renderLink` prop every `@bison-lab/ui` block takes.\n * The ui blocks hand over `target`/`rel` already expanded, so `newTab` is\n * read back off `target` to keep the flag and the attributes in step.\n */\nexport function renderLinkWith(Link: BlockLinkComponent): RenderLink {\n return ({ href, children, ...rest }) => (\n <Link href={href} newTab={rest.target === \"_blank\"} {...rest}>\n {children}\n </Link>\n );\n}\n","import type { ComponentType, ReactElement } from \"react\";\n\nimport { type BlockImageComponent, DefaultBlockImage } from \"./image\";\nimport { type BlockLinkComponent, DefaultBlockLink } from \"./link\";\n\n/** The minimum a row must be for the dispatch to place it. */\nexport interface BlockLike {\n blockType: string;\n id?: string | null;\n}\n\n/**\n * What every renderer is handed.\n *\n * The neighbour props let a block adapt to what sits around it: `prevType` to\n * close a seam against the block above, `runIndex` to alternate surfaces across\n * back-to-back blocks of one type, `isLast` because the final block sits\n * against the site footer.\n *\n * `containerClassName`, `imageComponent` and `linkComponent` are the three\n * things a package block cannot decide for itself — the site's page measure,\n * how an image gets optimised, and which router a link goes through. All three\n * arrive already defaulted, so a renderer never checks them.\n */\nexport interface BlockRendererProps<B extends BlockLike = BlockLike> {\n block: B;\n /** Position on the page, hero included. */\n index: number;\n /** The type of the block before this one; absent on the first block. */\n prevType?: string;\n /** Position within a run of consecutive same-type blocks, from 0. */\n runIndex: number;\n isLast: boolean;\n /** The site's page measure, for the band wrapper. */\n containerClassName: string;\n /** How this block turns a resolved image into an element. */\n imageComponent: BlockImageComponent;\n /** How this block turns an `href` into an element. */\n linkComponent: BlockLinkComponent;\n}\n\n/**\n * One renderer per block type, each typed to its own block.\n *\n * Written as a mapped type over the union so `hero` gets `HeroBlockData`, not\n * the whole union. A site writes `satisfies BlockRegistryFor<AnyBlock>` where\n * `AnyBlock` comes off its *generated* `Page` type — that is the compile-time\n * layout lock, and it has to stay in the site, because only the site has\n * generated types. The registry is a parameter here for exactly that reason.\n */\nexport type BlockRegistryFor<B extends BlockLike> = {\n [K in B[\"blockType\"]]: ComponentType<\n BlockRendererProps<Extract<B, { blockType: K }>>\n >;\n};\n\n/** Fallback page measure for a site that does not pass its own. */\nexport const DEFAULT_CONTAINER = \"mx-auto w-full max-w-7xl px-6\";\n\ntype LooseRenderer = ComponentType<BlockRendererProps<BlockLike>>;\n\n/**\n * A row saved against a block that has since left the config has no renderer.\n * It is dropped before anything is counted, so its neighbours see each other,\n * not a gap: the block after it gets the real `prevType`, a run of one type is\n * not broken by it, and the block before it is still `isLast` when it ends the\n * page.\n */\nfunction renderable(\n blocks: BlockLike[],\n registry: Record<string, LooseRenderer | undefined>,\n): { block: BlockLike; Renderer: LooseRenderer }[] {\n return blocks.flatMap((block) => {\n const Renderer = registry[block.blockType];\n return Renderer ? [{ block, Renderer }] : [];\n });\n}\n\n/** Position of each block within its run of consecutive same-type blocks. */\nfunction runIndexes(blocks: BlockLike[]): number[] {\n const out: number[] = [];\n for (let i = 0; i < blocks.length; i += 1) {\n out.push(\n i > 0 && blocks[i - 1].blockType === blocks[i].blockType\n ? out[i - 1] + 1\n : 0,\n );\n }\n return out;\n}\n\nexport interface RenderBlocksProps<B extends BlockLike> {\n /** Pass `[...page.hero, ...page.layout]`, so the hero counts as index 0. */\n blocks: B[];\n registry: BlockRegistryFor<B>;\n /** The site's page measure. Defaults to `DEFAULT_CONTAINER`. */\n containerClassName?: string;\n /** Defaults to a plain `<img>`; a Next site passes a `next/image` adapter. */\n imageComponent?: BlockImageComponent;\n /** Defaults to a plain `<a>`; a Next site passes a `next/link` adapter. */\n linkComponent?: BlockLinkComponent;\n}\n\n/**\n * Renders a page's blocks in order through the registry. A Server Component:\n * it only maps and looks up, and each renderer decides its own nature.\n */\nexport function RenderBlocks<B extends BlockLike>({\n blocks,\n registry,\n containerClassName = DEFAULT_CONTAINER,\n imageComponent = DefaultBlockImage,\n linkComponent = DefaultBlockLink,\n}: RenderBlocksProps<B>): ReactElement {\n const rows = renderable(\n blocks,\n // The mapped registry type is per-block, and the dispatch is not; the\n // lookup below is what guarantees each renderer only ever sees its own\n // block type, and no signature can express that to the compiler.\n registry as unknown as Record<string, LooseRenderer | undefined>,\n );\n const runs = runIndexes(rows.map((row) => row.block));\n return (\n <>\n {rows.map(({ block, Renderer }, index) => (\n <Renderer\n key={block.id ?? index}\n block={block}\n index={index}\n prevType={index > 0 ? rows[index - 1].block.blockType : undefined}\n runIndex={runs[index]}\n isLast={index === rows.length - 1}\n containerClassName={containerClassName}\n imageComponent={imageComponent}\n linkComponent={linkComponent}\n />\n ))}\n </>\n );\n}\n","/**\n * The upload side of the boundary.\n *\n * A Payload upload field holds either the row's id or, once depth resolves it,\n * the whole document. Neither shape can be imported from here: `Media` is\n * generated per site, and this package must never reach for a site's\n * `payload-types`. So the doc is described structurally, and every renderer\n * goes through `resolveMedia` rather than reading `.url` itself.\n */\n\n/**\n * The subset of a Payload upload document a block renderer actually reads.\n * Every site's generated `Media` interface is assignable to this, whatever\n * else it carries.\n *\n * Deliberately no index signature: TypeScript refuses to assign an interface\n * to a type with one, and a site's generated `Media` is always an interface.\n * An index signature here would break exactly the structural match this type\n * exists to provide.\n */\nexport interface MediaDoc {\n url?: string | null;\n alt?: string | null;\n width?: number | null;\n height?: number | null;\n}\n\n/** What an upload field holds before and after `depth` populates it. */\nexport type MediaValue = number | string | MediaDoc | null | undefined;\n\n/** A resolved image, in the shape every `@bison-lab/ui` block asks for. */\nexport interface ResolvedMedia {\n src: string;\n alt: string;\n width?: number;\n height?: number;\n}\n\nfunction isMediaDoc(value: MediaValue): value is MediaDoc {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Narrows an upload value to something renderable, or `undefined`.\n *\n * `undefined` covers three cases a renderer must treat identically: the field\n * is empty, `depth: 0` left it as a bare id, or the document exists but has no\n * `url` yet (an upload mid-flight). A renderer that gets `undefined` omits the\n * image rather than emitting a broken `<img>`.\n *\n * `alt` is always a string: the media collection requires it, but a draft saved\n * before that validation ran can still reach a renderer, and an `alt` of\n * `undefined` would silently drop the attribute entirely.\n */\nexport function resolveMedia(value: MediaValue): ResolvedMedia | undefined {\n if (!isMediaDoc(value)) return undefined;\n const { url, alt, width, height } = value;\n if (typeof url !== \"string\" || url.length === 0) return undefined;\n return {\n src: url,\n alt: typeof alt === \"string\" ? alt : \"\",\n ...(typeof width === \"number\" ? { width } : {}),\n ...(typeof height === \"number\" ? { height } : {}),\n };\n}\n","import { Button } from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { newTabAttributes } from \"../../link\";\nimport { resolveMedia } from \"../../media\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport type { HeroBlockData } from \"../../types\";\n\n/**\n * How each tone paints. Semantic tokens only — a site's brand reaches these\n * through its theme, never through a class named here.\n */\nconst TONES = {\n sweep: \"bg-primary text-primary-foreground\",\n dark: \"bg-foreground text-background\",\n light: \"bg-background text-foreground\",\n} as const;\n\n/**\n * The package's hero: deliberately plain.\n *\n * A hero is where a site's brand is loudest, so this exists to make a page\n * render at all, not to be the final answer. A site overrides this one registry\n * entry with its own and keeps every other block.\n */\nexport function HeroBlockRenderer({\n block,\n index,\n containerClassName,\n imageComponent: Image,\n linkComponent: Link,\n}: BlockRendererProps<HeroBlockData>) {\n const image = resolveMedia(block.image);\n const links = (block.links ?? []).filter((link) => link.href && link.label);\n const tone = TONES[block.tone ?? \"sweep\"];\n\n return (\n <section className={cx(\"relative isolate overflow-hidden\", tone)}>\n {image ? (\n <div className=\"absolute inset-0 -z-10 opacity-25\">\n <Image\n src={image.src}\n alt=\"\"\n width={image.width}\n height={image.height}\n className=\"h-full w-full object-cover\"\n sizes=\"100vw\"\n // The hero is the page's LCP element whenever it opens the page.\n priority={index === 0}\n />\n </div>\n ) : null}\n <div className={cx(containerClassName, \"py-20 lg:py-28\")}>\n <div className=\"max-w-3xl\">\n {block.eyebrow ? (\n <p className=\"text-sm font-semibold tracking-widest uppercase opacity-80\">\n {block.eyebrow}\n </p>\n ) : null}\n <h1 className=\"mt-3 text-4xl font-bold tracking-tight text-balance lg:text-5xl\">\n {block.heading}\n </h1>\n {block.body ? (\n <p className=\"mt-6 max-w-2xl text-lg leading-relaxed opacity-90\">\n {block.body}\n </p>\n ) : null}\n {links.length > 0 ? (\n <div className=\"mt-8 flex flex-wrap gap-3\">\n {links.map((link, i) => (\n <Button\n key={`${link.href}-${i}`}\n asChild\n variant={i === 0 ? \"default\" : \"outline\"}\n >\n <Link\n href={link.href as string}\n newTab={link.newTab ?? false}\n {...newTabAttributes(link.newTab)}\n >\n {link.label}\n </Link>\n </Button>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n </section>\n );\n}\n","import { ShowcasePanelsBlock, type ShowcasePanelItem } from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { renderLinkWith } from \"../../link\";\nimport { resolveMedia, type ResolvedMedia } from \"../../media\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport type { ShowcasePanelsBlockData } from \"../../types\";\n\nexport function ShowcasePanelsBlockRenderer({\n block,\n containerClassName,\n imageComponent: Image,\n linkComponent,\n}: BlockRendererProps<ShowcasePanelsBlockData>) {\n // A panel without a picture has nothing to expand into, so a row whose\n // upload has not resolved is dropped rather than rendered empty. `dims`\n // stays index-aligned with `items` so the image callback can reach the\n // width and height the library's own prop shape has no room for.\n const dims: ResolvedMedia[] = [];\n const items: ShowcasePanelItem[] = [];\n for (const [i, row] of (block.items ?? []).entries()) {\n const image = resolveMedia(row.image);\n if (!image) continue;\n dims.push(image);\n items.push({\n id: row.id ?? String(i),\n title: row.title ?? \"\",\n summary: row.summary ?? \"\",\n image: { src: image.src, alt: image.alt },\n ...(row.href ? { href: row.href } : {}),\n ...(row.numeral ? { numeral: row.numeral } : {}),\n });\n }\n if (items.length === 0) return null;\n\n return (\n <section className={cx(containerClassName, \"py-16 lg:py-24\")}>\n <ShowcasePanelsBlock\n items={items}\n spineVariant={block.spineVariant ?? \"numbered\"}\n defaultActiveIndex={Math.min(\n Math.max(block.defaultActiveIndex ?? 0, 0),\n items.length - 1,\n )}\n {...(block.watermark ? { watermark: block.watermark } : {})}\n renderLink={renderLinkWith(linkComponent)}\n renderImage={(item, i) => (\n <Image\n src={item.image.src}\n alt={item.image.alt ?? \"\"}\n width={dims[i]?.width}\n height={dims[i]?.height}\n className=\"absolute inset-0 h-full w-full object-cover\"\n sizes=\"(min-width: 1024px) 50vw, 100vw\"\n />\n )}\n />\n </section>\n );\n}\n","import { ProcessStepsBlock, type ProcessStepItem } from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { resolveMedia, type ResolvedMedia } from \"../../media\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport type { ProcessStepsBlockData } from \"../../types\";\n\nexport function ProcessStepsBlockRenderer({\n block,\n containerClassName,\n imageComponent: Image,\n}: BlockRendererProps<ProcessStepsBlockData>) {\n // Same rule as the showcase panels: a step is its picture, so an unresolved\n // upload drops the row instead of rendering a hole in the rail.\n const dims: ResolvedMedia[] = [];\n const items: ProcessStepItem[] = [];\n for (const [i, row] of (block.items ?? []).entries()) {\n const image = resolveMedia(row.image);\n if (!image) continue;\n dims.push(image);\n items.push({\n id: row.id ?? String(i),\n title: row.title ?? \"\",\n description: row.description ?? \"\",\n image: { src: image.src, alt: image.alt },\n });\n }\n if (items.length === 0) return null;\n\n return (\n <section className={cx(containerClassName, \"py-16 lg:py-24\")}>\n <ProcessStepsBlock\n items={items}\n autoAdvance={block.autoAdvance ?? true}\n autoAdvanceDuration={block.autoAdvanceDuration ?? 6000}\n pauseOnHover={block.pauseOnHover ?? true}\n defaultActiveIndex={Math.min(\n Math.max(block.defaultActiveIndex ?? 0, 0),\n items.length - 1,\n )}\n renderImage={(item, i) => (\n <Image\n src={item.image.src}\n alt={item.image.alt ?? \"\"}\n width={dims[i]?.width}\n height={dims[i]?.height}\n className=\"size-full object-cover\"\n sizes=\"(min-width: 768px) 66vw, 100vw\"\n />\n )}\n />\n </section>\n );\n}\n","import { FAQColumnsBlock, type FAQItem } from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { renderLinkWith } from \"../../link\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport type { FaqColumnsBlockData } from \"../../types\";\n\nexport function FaqColumnsBlockRenderer({\n block,\n containerClassName,\n linkComponent,\n}: BlockRendererProps<FaqColumnsBlockData>) {\n const items: FAQItem[] = (block.items ?? [])\n .filter((row) => row.question && row.answer)\n .map((row, i) => ({\n id: row.id ?? String(i),\n question: row.question as string,\n // The field is a textarea, so blank lines are the editor's paragraph\n // breaks and have to survive into the DOM.\n answer: <span className=\"whitespace-pre-line\">{row.answer}</span>,\n }));\n if (items.length === 0) return null;\n\n const cta = block.cta;\n const hasCta = Boolean(cta?.href && cta?.linkText);\n\n return (\n <section>\n <div className={cx(containerClassName, \"py-16 lg:py-24\")}>\n <FAQColumnsBlock\n // The site measure is on the wrapper above; the block's own\n // container and vertical rhythm are reset so they cannot fight it.\n className=\"py-0 md:py-0\"\n containerClassName=\"max-w-none px-0 sm:px-0\"\n items={items}\n title={block.title ?? \"\"}\n {...(block.eyebrow ? { eyebrow: block.eyebrow } : {})}\n {...(block.description ? { description: block.description } : {})}\n sticky={block.sticky ?? true}\n type={block.type ?? \"single\"}\n collapsible={block.collapsible ?? true}\n renderLink={renderLinkWith(linkComponent)}\n {...(hasCta && cta\n ? {\n cta: {\n title: cta.title ?? \"\",\n linkText: cta.linkText as string,\n href: cta.href as string,\n newTab: cta.newTab ?? false,\n },\n }\n : {})}\n />\n </div>\n </section>\n );\n}\n","import { TestimonialMasonry, type TestimonialItem } from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { renderLinkWith } from \"../../link\";\nimport { resolveMedia } from \"../../media\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport type { TestimonialMasonryBlockData } from \"../../types\";\n\nexport function TestimonialMasonryBlockRenderer({\n block,\n containerClassName,\n linkComponent,\n}: BlockRendererProps<TestimonialMasonryBlockData>) {\n const items: TestimonialItem[] = (block.items ?? [])\n .filter((row) => row.content && row.author?.name)\n .map((row, i) => {\n const avatar = resolveMedia(row.author?.avatar);\n return {\n id: row.id ?? String(i),\n content: row.content as string,\n author: {\n name: row.author?.name as string,\n ...(row.author?.title ? { title: row.author.title } : {}),\n // The library falls back to initials when this is absent, which is\n // why an unresolved upload must not become an empty string.\n ...(avatar ? { avatarUrl: avatar.src } : {}),\n },\n };\n });\n if (items.length === 0) return null;\n\n const link = block.link;\n const hasLink = Boolean(link?.href && link?.label);\n\n return (\n // The band's surface is full-bleed; only its contents sit at the measure.\n <section className=\"bg-accent\">\n <div className={cx(containerClassName, \"py-16 lg:py-24\")}>\n <TestimonialMasonry\n className=\"bg-transparent px-0 lg:px-0\"\n containerClassName=\"max-w-none px-0 py-0 sm:py-0 md:px-0 md:py-0\"\n items={items}\n title={block.title ?? \"\"}\n renderLink={renderLinkWith(linkComponent)}\n {...(block.eyebrow ? { eyebrow: block.eyebrow } : {})}\n {...(block.description ? { description: block.description } : {})}\n {...(typeof block.minItemsForFade === \"number\"\n ? { minItemsForFade: block.minItemsForFade }\n : {})}\n {...(typeof block.maxVisibleRows === \"number\"\n ? { maxVisibleRows: block.maxVisibleRows }\n : {})}\n {...(hasLink && link\n ? {\n link: {\n label: link.label as string,\n href: link.href as string,\n newTab: link.newTab ?? false,\n },\n }\n : {})}\n />\n </div>\n </section>\n );\n}\n","import {\n JsonLd,\n NapBlock,\n type NapDepartment,\n type NapRecord,\n} from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { renderLinkWith } from \"../../link\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport type { NapBlockData } from \"../../types\";\n\nexport function NapBlockRenderer({\n block,\n containerClassName,\n linkComponent,\n}: BlockRendererProps<NapBlockData>) {\n const address = block.address;\n const departments: NapDepartment[] = (block.departments ?? [])\n // `phoneE164` is what becomes the `tel:` href and the structured data's\n // `telephone`, so a department without one is not publishable as a NAP\n // entry at all — dropping it beats emitting an unreachable number.\n .filter((row) => row.name && row.phoneE164)\n .map((row, i) => ({\n id: row.id ?? String(i),\n name: row.name as string,\n ...(row.departmentType ? { type: row.departmentType } : {}),\n phone: {\n e164: row.phoneE164 as string,\n ...(row.phoneDisplay ? { display: row.phoneDisplay } : {}),\n },\n }));\n\n if (!block.businessName || !address?.streetAddress || departments.length === 0)\n return null;\n\n const record: NapRecord = {\n name: block.businessName,\n type: block.businessType ?? \"LocalBusiness\",\n address: {\n streetAddress: address.streetAddress,\n addressLocality: address.addressLocality ?? \"\",\n addressRegion: address.addressRegion ?? \"\",\n postalCode: address.postalCode ?? \"\",\n ...(address.addressCountry\n ? { addressCountry: address.addressCountry }\n : {}),\n },\n departments,\n ...(block.url ? { url: block.url } : {}),\n };\n\n return (\n <section>\n <div className={cx(containerClassName, \"py-16 lg:py-24\")}>\n <NapBlock\n record={record}\n showName={block.showName ?? true}\n headingLevel={block.headingLevel ?? \"h2\"}\n renderLink={renderLinkWith(linkComponent)}\n />\n {block.emitJsonLd === false ? null : <JsonLd data={record} />}\n </div>\n </section>\n );\n}\n","/**\n * The one rule for a typed panel width, shared by the config's validator and\n * the renderer's reader so the two cannot drift: if the validator accepted a\n * value the renderer dropped, the editor would be back to a width that\n * silently does nothing. React-free, because `config.ts` ships from the Node\n * entry.\n */\nconst REM_WIDTH = /^\\s*(\\d+(?:\\.\\d+)?)\\s*(rem)?\\s*$/;\n\n/** \"30\" and \"30rem\" both mean 30rem; anything else is not a width. */\nexport function remWidth(\n value: string | null | undefined,\n): `${number}rem` | null {\n const match = REM_WIDTH.exec(value ?? \"\");\n return match ? (`${Number(match[1])}rem` as `${number}rem`) : null;\n}\n","import {\n MegaMenuPanel,\n type MegaMenuColumn,\n type MegaMenuIcons,\n type MegaMenuLink,\n type MegaMenuPanelData,\n type MegaMenuSection,\n type MegaMenuVariants,\n type MegaMenuWidth,\n} from \"@bison-lab/ui\";\n\nimport { renderLinkWith } from \"../../link\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport type { MegaMenuBlockData, MegaMenuLinkData } from \"../../types\";\nimport { remWidth } from \"./rem-width\";\n\nfunction completeLink(row: MegaMenuLinkData): MegaMenuLink | null {\n if (!row.label || !row.href) return null;\n return {\n label: row.label,\n href: row.href,\n ...(row.description ? { description: row.description } : {}),\n ...(row.variant ? { variant: row.variant } : {}),\n ...(row.icon ? { icon: row.icon } : {}),\n ...(row.newTab ? { newTab: true } : {}),\n };\n}\n\n/**\n * The row as `MegaMenuPanel` data, or `null` when there is nothing to open:\n * no columns, or columns whose links are not yet complete. Draft saves skip\n * validation, so a live preview genuinely hands this half-built rows.\n */\nexport function megaMenuPanelFromRow(\n row: MegaMenuBlockData,\n): MegaMenuPanelData | null {\n const panel = row.panel;\n if (!panel?.columns?.length) return null;\n const custom = Boolean(panel.customWidths);\n\n const columns: MegaMenuColumn[] = [];\n for (const column of panel.columns) {\n const sections: MegaMenuSection[] = [];\n for (const section of column.sections ?? []) {\n const links = (section.links ?? [])\n .map(completeLink)\n .filter((link): link is MegaMenuLink => link !== null);\n if (!links.length) continue;\n sections.push({\n ...(section.eyebrow ? { eyebrow: section.eyebrow } : {}),\n display: section.display ?? \"list\",\n hideDescriptionsOnMobile: section.hideDescriptionsOnMobile ?? true,\n links,\n });\n }\n columns.push({\n ...(custom && column.customWidth\n ? { customWidth: column.customWidth }\n : { width: Number(column.width ?? \"100\") as MegaMenuWidth }),\n ...(column.divider ? { divider: true } : {}),\n sections,\n });\n }\n if (!columns.some((column) => column.sections.length)) return null;\n\n const overview = panel.footer?.overview;\n const cta = panel.footer?.cta;\n // Independent of the column checkbox: the config gates only `customWidth`\n // on it, and the help text promises a typed panel width is used.\n const customMax = remWidth(panel.customMaxWidth);\n\n return {\n maxWidth: customMax ?? panel.maxWidth ?? \"standard\",\n columns,\n footer: {\n ...(overview?.label && overview.href\n ? {\n overview: {\n label: overview.label,\n href: overview.href,\n ...(overview.newTab ? { newTab: true } : {}),\n },\n }\n : {}),\n ...(cta?.label && cta.href\n ? {\n cta: {\n label: cta.label,\n href: cta.href,\n ...(cta.newTab ? { newTab: true } : {}),\n },\n }\n : {}),\n },\n };\n}\n\nexport interface MegaMenuBlockRendererProps\n extends BlockRendererProps<MegaMenuBlockData> {\n /** The look behind each `variants` value the site passed to `megaMenuBlock`. */\n variants?: MegaMenuVariants;\n /** The glyph behind each `icons` value the site passed to `megaMenuBlock`. */\n icons?: MegaMenuIcons;\n}\n\n/**\n * One panel, standing alone: what a live preview shows while the row is being\n * edited. A whole header goes through `headerItemsFromBlocks` instead.\n */\nexport function MegaMenuBlockRenderer({\n block,\n linkComponent,\n variants,\n icons,\n}: MegaMenuBlockRendererProps) {\n const panel = megaMenuPanelFromRow(block);\n if (!panel) return null;\n return (\n <MegaMenuPanel\n {...panel}\n variants={variants}\n icons={icons}\n renderLink={renderLinkWith(linkComponent)}\n />\n );\n}\n","import {\n megaMenuToFloatingNavItems,\n type FloatingNavItem,\n type MegaMenuIcons,\n type MegaMenuItem,\n type MegaMenuVariants,\n} from \"@bison-lab/ui\";\n\nimport { renderLinkWith, type BlockLinkComponent } from \"../../link\";\nimport type { MegaMenuBlockData, NavLinkBlockData } from \"../../types\";\nimport { megaMenuPanelFromRow } from \"./component\";\n\n/** A row of a header global's `items`: either block, or whatever else a site added. */\nexport type HeaderBlockRow = MegaMenuBlockData | NavLinkBlockData;\n\nexport interface HeaderItemsOptions {\n variants?: MegaMenuVariants;\n icons?: MegaMenuIcons;\n /** Marks the current section; receives each item's `href`. */\n isActive?: (href: string) => boolean;\n /** The site's link adapter, applied to every link inside the panels. */\n linkComponent?: BlockLinkComponent;\n}\n\n/**\n * A global's `items` as `FloatingNavBlock` items, so a site's header is one\n * call. Rows the bar cannot show are dropped rather than rendered broken: a\n * row with no label, a mega menu with nothing to open, and any block type a\n * site added that this package does not know.\n *\n * A `navLink` row's `newTab` is not carried: `FloatingNavItem` has no such\n * field, because the bar renders its own links.\n */\nexport function headerItemsFromBlocks(\n blocks: HeaderBlockRow[],\n { variants, icons, isActive, linkComponent }: HeaderItemsOptions = {},\n): FloatingNavItem[] {\n const items: MegaMenuItem[] = [];\n for (const row of blocks) {\n if (!row.label) continue;\n if (row.blockType === \"navLink\") {\n if (!row.href) continue;\n items.push({ label: row.label, href: row.href });\n } else if (row.blockType === \"megaMenu\") {\n const panel = megaMenuPanelFromRow(row);\n if (!panel) continue;\n items.push({\n label: row.label,\n ...(row.href ? { href: row.href } : {}),\n panel,\n });\n }\n }\n return megaMenuToFloatingNavItems(\n { items },\n {\n isActive,\n variants,\n icons,\n ...(linkComponent ? { renderLink: renderLinkWith(linkComponent) } : {}),\n },\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"react.mjs","names":[],"sources":["../src/image.tsx","../src/link.tsx","../src/render-blocks.tsx","../src/media.ts","../src/blocks/hero/component.tsx","../src/blocks/showcase-panels/component.tsx","../src/blocks/process-steps/component.tsx","../src/blocks/faq-columns/component.tsx","../src/blocks/testimonial-masonry/component.tsx","../src/blocks/nap/component.tsx","../src/blocks/stats-band/component.tsx","../src/blocks/mega-menu/rem-width.ts","../src/blocks/mega-menu/component.tsx","../src/blocks/mega-menu/header.tsx"],"sourcesContent":["import type { ComponentType } from \"react\";\n\n/**\n * The image injection point.\n *\n * The package deliberately does not depend on `next` — a Payload site is not\n * necessarily a Next.js site, and `next/image` needs per-site configuration\n * (`remotePatterns` for the media route) that a package cannot supply. So\n * renderers never construct an image element themselves; they render the\n * component they were handed. A Next site writes one adapter around\n * `next/image` and every block gets optimisation from it.\n *\n * This is a *component type*, not a render function, on purpose: a Server\n * Component may pass a client component reference across the boundary, but not\n * a closure. `imageComponent` therefore survives the trip from a server page\n * into these client renderers, where a `renderImage` callback would not.\n */\n\nexport interface BlockImageProps {\n src: string;\n /** Always a string — see `resolveMedia`. Empty means decorative. */\n alt: string;\n width?: number;\n height?: number;\n className?: string;\n /** Passed through to `next/image`'s `sizes`; ignored by the default. */\n sizes?: string;\n /** Set on the one image above the fold, typically the hero's. */\n priority?: boolean;\n}\n\nexport type BlockImageComponent = ComponentType<BlockImageProps>;\n\n/**\n * The fallback when no `imageComponent` is supplied: a plain `<img>`. Correct\n * everywhere and optimised nowhere, which is the right default for a package\n * that cannot know what framework it landed in.\n */\nexport function DefaultBlockImage({\n src,\n alt,\n width,\n height,\n className,\n sizes,\n priority,\n}: BlockImageProps) {\n return (\n <img\n src={src}\n alt={alt}\n width={width}\n height={height}\n className={className}\n sizes={sizes}\n loading={priority ? \"eager\" : \"lazy\"}\n decoding=\"async\"\n />\n );\n}\n","import type { AnchorHTMLAttributes, ComponentType, ReactNode } from \"react\";\nimport type { RenderLink } from \"@bison-lab/ui\";\n\n/**\n * The link injection point, the same shape as `BlockImageComponent`.\n *\n * A renderer never writes `<a>` directly. A site hands in one link component\n * and every CMS link routes through it — Next's `Link`, so navigation stays\n * client-side. It is a *component type*, not a render function, for the same\n * reason the image seam is: a Server Component can pass a client component\n * reference across the boundary, but not a closure.\n *\n * `newTab` is the flag from `linkFields()`; `target` and `rel` are always\n * already expanded from it by the time the component is called, so an adapter\n * that only spreads the anchor attributes onto its router link is still safe.\n * The one thing an adapter must do is drop `newTab` before spreading, or React\n * warns about an unknown DOM attribute — see the README's `next/link` adapter.\n */\nexport interface BlockLinkProps\n extends AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string;\n children: ReactNode;\n newTab?: boolean;\n className?: string;\n}\n\nexport type BlockLinkComponent = ComponentType<BlockLinkProps>;\n\n/** `target` and `rel` together, or neither: a bare `target` is a tabnabbing hole. */\nexport function newTabAttributes(\n newTab: boolean | null | undefined,\n): Pick<AnchorHTMLAttributes<HTMLAnchorElement>, \"target\" | \"rel\"> {\n return newTab ? { target: \"_blank\", rel: \"noopener noreferrer\" } : {};\n}\n\n/**\n * The fallback when no `linkComponent` is supplied: a plain `<a>`. Correct\n * everywhere and client-side nowhere, which is the right default for a package\n * that cannot know what router it landed in.\n */\nexport function DefaultBlockLink({\n href,\n newTab,\n children,\n ...rest\n}: BlockLinkProps) {\n return (\n <a href={href} {...rest} {...newTabAttributes(newTab)}>\n {children}\n </a>\n );\n}\nDefaultBlockLink.displayName = \"DefaultBlockLink\";\n\n/**\n * Adapts the seam to the `renderLink` prop every `@bison-lab/ui` block takes.\n * The ui blocks hand over `target`/`rel` already expanded, so `newTab` is\n * read back off `target` to keep the flag and the attributes in step.\n */\nexport function renderLinkWith(Link: BlockLinkComponent): RenderLink {\n return ({ href, children, ...rest }) => (\n <Link href={href} newTab={rest.target === \"_blank\"} {...rest}>\n {children}\n </Link>\n );\n}\n","import type { ComponentType, ReactElement } from \"react\";\n\nimport { type BlockImageComponent, DefaultBlockImage } from \"./image\";\nimport { type BlockLinkComponent, DefaultBlockLink } from \"./link\";\nimport { overlapsSeam } from \"./seam\";\n\n/** The minimum a row must be for the dispatch to place it. */\nexport interface BlockLike {\n blockType: string;\n id?: string | null;\n}\n\n/**\n * What every renderer is handed.\n *\n * The neighbour props let a block adapt to what sits around it: `prevType` to\n * close a seam against the block above, `runIndex` to alternate surfaces across\n * back-to-back blocks of one type, `isLast` because the final block sits\n * against the site footer, and `overlapAbove` / `overlapBelow` when the\n * neighbour on that side floats across the seam (see `seam.ts`).\n *\n * `containerClassName`, `imageComponent` and `linkComponent` are the three\n * things a package block cannot decide for itself — the site's page measure,\n * how an image gets optimised, and which router a link goes through. All three\n * arrive already defaulted, so a renderer never checks them.\n */\nexport interface BlockRendererProps<B extends BlockLike = BlockLike> {\n block: B;\n /** Position on the page, hero included. */\n index: number;\n /** The type of the block before this one; absent on the first block. */\n prevType?: string;\n /** Position within a run of consecutive same-type blocks, from 0. */\n runIndex: number;\n isLast: boolean;\n /**\n * The block before this one floats down over the seam, so this block's band\n * wrapper needs clearance at the top. Decided by `RenderBlocks`' `floats`.\n */\n overlapAbove?: boolean;\n /** The block after this one floats up over the seam; clearance at the bottom. */\n overlapBelow?: boolean;\n /** The site's page measure, for the band wrapper. */\n containerClassName: string;\n /** How this block turns a resolved image into an element. */\n imageComponent: BlockImageComponent;\n /** How this block turns an `href` into an element. */\n linkComponent: BlockLinkComponent;\n}\n\n/**\n * One renderer per block type, each typed to its own block.\n *\n * Written as a mapped type over the union so `hero` gets `HeroBlockData`, not\n * the whole union. A site writes `satisfies BlockRegistryFor<AnyBlock>` where\n * `AnyBlock` comes off its *generated* `Page` type — that is the compile-time\n * layout lock, and it has to stay in the site, because only the site has\n * generated types. The registry is a parameter here for exactly that reason.\n */\nexport type BlockRegistryFor<B extends BlockLike> = {\n [K in B[\"blockType\"]]: ComponentType<\n BlockRendererProps<Extract<B, { blockType: K }>>\n >;\n};\n\n/** Fallback page measure for a site that does not pass its own. */\nexport const DEFAULT_CONTAINER = \"mx-auto w-full max-w-7xl px-6\";\n\ntype LooseRenderer = ComponentType<BlockRendererProps<BlockLike>>;\n\n/**\n * A row saved against a block that has since left the config has no renderer.\n * It is dropped before anything is counted, so its neighbours see each other,\n * not a gap: the block after it gets the real `prevType`, a run of one type is\n * not broken by it, and the block before it is still `isLast` when it ends the\n * page.\n */\nfunction renderable(\n blocks: BlockLike[],\n registry: Record<string, LooseRenderer | undefined>,\n): { block: BlockLike; Renderer: LooseRenderer }[] {\n return blocks.flatMap((block) => {\n const Renderer = registry[block.blockType];\n return Renderer ? [{ block, Renderer }] : [];\n });\n}\n\n/** Position of each block within its run of consecutive same-type blocks. */\nfunction runIndexes(blocks: BlockLike[]): number[] {\n const out: number[] = [];\n for (let i = 0; i < blocks.length; i += 1) {\n out.push(\n i > 0 && blocks[i - 1].blockType === blocks[i].blockType\n ? out[i - 1] + 1\n : 0,\n );\n }\n return out;\n}\n\nexport interface RenderBlocksProps<B extends BlockLike> {\n /** Pass `[...page.hero, ...page.layout]`, so the hero counts as index 0. */\n blocks: B[];\n registry: BlockRegistryFor<B>;\n /** The site's page measure. Defaults to `DEFAULT_CONTAINER`. */\n containerClassName?: string;\n /** Defaults to a plain `<img>`; a Next site passes a `next/image` adapter. */\n imageComponent?: BlockImageComponent;\n /** Defaults to a plain `<a>`; a Next site passes a `next/link` adapter. */\n linkComponent?: BlockLinkComponent;\n /**\n * Which rows float across their seams, so their neighbours make room.\n * Defaults to `overlapsSeam`: the package's stats band with `overlap` on. A\n * site with its own floating block extends it:\n * `floats={(row) => overlapsSeam(row) || row.blockType === \"bookingCard\"}`.\n */\n floats?: (block: B) => boolean;\n}\n\n/**\n * Renders a page's blocks in order through the registry. A Server Component:\n * it only maps and looks up, and each renderer decides its own nature.\n */\nexport function RenderBlocks<B extends BlockLike>({\n blocks,\n registry,\n containerClassName = DEFAULT_CONTAINER,\n imageComponent = DefaultBlockImage,\n linkComponent = DefaultBlockLink,\n floats = overlapsSeam,\n}: RenderBlocksProps<B>): ReactElement {\n const rows = renderable(\n blocks,\n // The mapped registry type is per-block, and the dispatch is not; the\n // lookup below is what guarantees each renderer only ever sees its own\n // block type, and no signature can express that to the compiler.\n registry as unknown as Record<string, LooseRenderer | undefined>,\n );\n const runs = runIndexes(rows.map((row) => row.block));\n const floating = rows.map((row) => floats(row.block as B));\n return (\n <>\n {rows.map(({ block, Renderer }, index) => (\n <Renderer\n key={block.id ?? index}\n block={block}\n index={index}\n prevType={index > 0 ? rows[index - 1].block.blockType : undefined}\n runIndex={runs[index]}\n isLast={index === rows.length - 1}\n overlapAbove={index > 0 && floating[index - 1]}\n overlapBelow={index < rows.length - 1 && floating[index + 1]}\n containerClassName={containerClassName}\n imageComponent={imageComponent}\n linkComponent={linkComponent}\n />\n ))}\n </>\n );\n}\n","/**\n * The upload side of the boundary.\n *\n * A Payload upload field holds either the row's id or, once depth resolves it,\n * the whole document. Neither shape can be imported from here: `Media` is\n * generated per site, and this package must never reach for a site's\n * `payload-types`. So the doc is described structurally, and every renderer\n * goes through `resolveMedia` rather than reading `.url` itself.\n */\n\n/**\n * The subset of a Payload upload document a block renderer actually reads.\n * Every site's generated `Media` interface is assignable to this, whatever\n * else it carries.\n *\n * Deliberately no index signature: TypeScript refuses to assign an interface\n * to a type with one, and a site's generated `Media` is always an interface.\n * An index signature here would break exactly the structural match this type\n * exists to provide.\n */\nexport interface MediaDoc {\n url?: string | null;\n alt?: string | null;\n width?: number | null;\n height?: number | null;\n}\n\n/** What an upload field holds before and after `depth` populates it. */\nexport type MediaValue = number | string | MediaDoc | null | undefined;\n\n/** A resolved image, in the shape every `@bison-lab/ui` block asks for. */\nexport interface ResolvedMedia {\n src: string;\n alt: string;\n width?: number;\n height?: number;\n}\n\nfunction isMediaDoc(value: MediaValue): value is MediaDoc {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Narrows an upload value to something renderable, or `undefined`.\n *\n * `undefined` covers three cases a renderer must treat identically: the field\n * is empty, `depth: 0` left it as a bare id, or the document exists but has no\n * `url` yet (an upload mid-flight). A renderer that gets `undefined` omits the\n * image rather than emitting a broken `<img>`.\n *\n * `alt` is always a string: the media collection requires it, but a draft saved\n * before that validation ran can still reach a renderer, and an `alt` of\n * `undefined` would silently drop the attribute entirely.\n */\nexport function resolveMedia(value: MediaValue): ResolvedMedia | undefined {\n if (!isMediaDoc(value)) return undefined;\n const { url, alt, width, height } = value;\n if (typeof url !== \"string\" || url.length === 0) return undefined;\n return {\n src: url,\n alt: typeof alt === \"string\" ? alt : \"\",\n ...(typeof width === \"number\" ? { width } : {}),\n ...(typeof height === \"number\" ? { height } : {}),\n };\n}\n","import { Button } from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { newTabAttributes } from \"../../link\";\nimport { resolveMedia } from \"../../media\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport { seamClasses } from \"../../seam\";\nimport type { HeroBlockData } from \"../../types\";\n\n/**\n * How each tone paints. Semantic tokens only — a site's brand reaches these\n * through its theme, never through a class named here.\n */\nconst TONES = {\n sweep: \"bg-primary text-primary-foreground\",\n dark: \"bg-foreground text-background\",\n light: \"bg-background text-foreground\",\n} as const;\n\n/**\n * The package's hero: deliberately plain.\n *\n * A hero is where a site's brand is loudest, so this exists to make a page\n * render at all, not to be the final answer. A site overrides this one registry\n * entry with its own and keeps every other block.\n */\nexport function HeroBlockRenderer({\n block,\n index,\n overlapAbove,\n overlapBelow,\n containerClassName,\n imageComponent: Image,\n linkComponent: Link,\n}: BlockRendererProps<HeroBlockData>) {\n const image = resolveMedia(block.image);\n const links = (block.links ?? []).filter((link) => link.href && link.label);\n const tone = TONES[block.tone ?? \"sweep\"];\n\n return (\n <section\n className={cx(\n \"relative isolate overflow-hidden\",\n tone,\n // Symmetric padding by default; the extra below appears only when an\n // editor drops an overlapping band under the hero.\n seamClasses({ overlapAbove, overlapBelow }),\n )}\n >\n {image ? (\n <div className=\"absolute inset-0 -z-10 opacity-25\">\n <Image\n src={image.src}\n alt=\"\"\n width={image.width}\n height={image.height}\n className=\"h-full w-full object-cover\"\n sizes=\"100vw\"\n // The hero is the page's LCP element whenever it opens the page.\n priority={index === 0}\n />\n </div>\n ) : null}\n <div className={cx(containerClassName, \"py-20 lg:py-28\")}>\n <div className=\"max-w-3xl\">\n {block.eyebrow ? (\n <p className=\"text-sm font-semibold tracking-widest uppercase opacity-80\">\n {block.eyebrow}\n </p>\n ) : null}\n <h1 className=\"mt-3 text-4xl font-bold tracking-tight text-balance lg:text-5xl\">\n {block.heading}\n </h1>\n {block.body ? (\n <p className=\"mt-6 max-w-2xl text-lg leading-relaxed opacity-90\">\n {block.body}\n </p>\n ) : null}\n {links.length > 0 ? (\n <div className=\"mt-8 flex flex-wrap gap-3\">\n {links.map((link, i) => (\n <Button\n key={`${link.href}-${i}`}\n asChild\n variant={i === 0 ? \"default\" : \"outline\"}\n >\n <Link\n href={link.href as string}\n newTab={link.newTab ?? false}\n {...newTabAttributes(link.newTab)}\n >\n {link.label}\n </Link>\n </Button>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n </section>\n );\n}\n","import { ShowcasePanelsBlock, type ShowcasePanelItem } from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { renderLinkWith } from \"../../link\";\nimport { resolveMedia, type ResolvedMedia } from \"../../media\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport { seamClasses } from \"../../seam\";\nimport type { ShowcasePanelsBlockData } from \"../../types\";\n\nexport function ShowcasePanelsBlockRenderer({\n block,\n overlapAbove,\n overlapBelow,\n containerClassName,\n imageComponent: Image,\n linkComponent,\n}: BlockRendererProps<ShowcasePanelsBlockData>) {\n // A panel without a picture has nothing to expand into, so a row whose\n // upload has not resolved is dropped rather than rendered empty. `dims`\n // stays index-aligned with `items` so the image callback can reach the\n // width and height the library's own prop shape has no room for.\n const dims: ResolvedMedia[] = [];\n const items: ShowcasePanelItem[] = [];\n for (const [i, row] of (block.items ?? []).entries()) {\n const image = resolveMedia(row.image);\n if (!image) continue;\n dims.push(image);\n items.push({\n id: row.id ?? String(i),\n title: row.title ?? \"\",\n summary: row.summary ?? \"\",\n image: { src: image.src, alt: image.alt },\n ...(row.href ? { href: row.href } : {}),\n ...(row.numeral ? { numeral: row.numeral } : {}),\n });\n }\n if (items.length === 0) return null;\n\n return (\n <section className={seamClasses({ overlapAbove, overlapBelow })}>\n <div className={cx(containerClassName, \"py-16 lg:py-24\")}>\n <ShowcasePanelsBlock\n items={items}\n spineVariant={block.spineVariant ?? \"numbered\"}\n defaultActiveIndex={Math.min(\n Math.max(block.defaultActiveIndex ?? 0, 0),\n items.length - 1,\n )}\n {...(block.watermark ? { watermark: block.watermark } : {})}\n renderLink={renderLinkWith(linkComponent)}\n renderImage={(item, i) => (\n <Image\n src={item.image.src}\n alt={item.image.alt ?? \"\"}\n width={dims[i]?.width}\n height={dims[i]?.height}\n className=\"absolute inset-0 h-full w-full object-cover\"\n sizes=\"(min-width: 1024px) 50vw, 100vw\"\n />\n )}\n />\n </div>\n </section>\n );\n}\n","import { ProcessStepsBlock, type ProcessStepItem } from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { resolveMedia, type ResolvedMedia } from \"../../media\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport { seamClasses } from \"../../seam\";\nimport type { ProcessStepsBlockData } from \"../../types\";\n\nexport function ProcessStepsBlockRenderer({\n block,\n overlapAbove,\n overlapBelow,\n containerClassName,\n imageComponent: Image,\n}: BlockRendererProps<ProcessStepsBlockData>) {\n // Same rule as the showcase panels: a step is its picture, so an unresolved\n // upload drops the row instead of rendering a hole in the rail.\n const dims: ResolvedMedia[] = [];\n const items: ProcessStepItem[] = [];\n for (const [i, row] of (block.items ?? []).entries()) {\n const image = resolveMedia(row.image);\n if (!image) continue;\n dims.push(image);\n items.push({\n id: row.id ?? String(i),\n title: row.title ?? \"\",\n description: row.description ?? \"\",\n image: { src: image.src, alt: image.alt },\n });\n }\n if (items.length === 0) return null;\n\n return (\n <section className={seamClasses({ overlapAbove, overlapBelow })}>\n <div className={cx(containerClassName, \"py-16 lg:py-24\")}>\n <ProcessStepsBlock\n items={items}\n autoAdvance={block.autoAdvance ?? true}\n autoAdvanceDuration={block.autoAdvanceDuration ?? 6000}\n pauseOnHover={block.pauseOnHover ?? true}\n defaultActiveIndex={Math.min(\n Math.max(block.defaultActiveIndex ?? 0, 0),\n items.length - 1,\n )}\n renderImage={(item, i) => (\n <Image\n src={item.image.src}\n alt={item.image.alt ?? \"\"}\n width={dims[i]?.width}\n height={dims[i]?.height}\n className=\"size-full object-cover\"\n sizes=\"(min-width: 768px) 66vw, 100vw\"\n />\n )}\n />\n </div>\n </section>\n );\n}\n","import { FAQColumnsBlock, type FAQItem } from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { renderLinkWith } from \"../../link\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport { seamClasses } from \"../../seam\";\nimport type { FaqColumnsBlockData } from \"../../types\";\n\nexport function FaqColumnsBlockRenderer({\n block,\n overlapAbove,\n overlapBelow,\n containerClassName,\n linkComponent,\n}: BlockRendererProps<FaqColumnsBlockData>) {\n const items: FAQItem[] = (block.items ?? [])\n .filter((row) => row.question && row.answer)\n .map((row, i) => ({\n id: row.id ?? String(i),\n question: row.question as string,\n // The field is a textarea, so blank lines are the editor's paragraph\n // breaks and have to survive into the DOM.\n answer: <span className=\"whitespace-pre-line\">{row.answer}</span>,\n }));\n if (items.length === 0) return null;\n\n const cta = block.cta;\n const hasCta = Boolean(cta?.href && cta?.linkText);\n\n return (\n <section className={seamClasses({ overlapAbove, overlapBelow })}>\n <div className={cx(containerClassName, \"py-16 lg:py-24\")}>\n <FAQColumnsBlock\n // The site measure is on the wrapper above; the block's own\n // container and vertical rhythm are reset so they cannot fight it.\n className=\"py-0 md:py-0\"\n containerClassName=\"max-w-none px-0 sm:px-0\"\n items={items}\n title={block.title ?? \"\"}\n {...(block.eyebrow ? { eyebrow: block.eyebrow } : {})}\n {...(block.description ? { description: block.description } : {})}\n sticky={block.sticky ?? true}\n type={block.type ?? \"single\"}\n collapsible={block.collapsible ?? true}\n renderLink={renderLinkWith(linkComponent)}\n {...(hasCta && cta\n ? {\n cta: {\n title: cta.title ?? \"\",\n linkText: cta.linkText as string,\n href: cta.href as string,\n newTab: cta.newTab ?? false,\n },\n }\n : {})}\n />\n </div>\n </section>\n );\n}\n","import { TestimonialMasonry, type TestimonialItem } from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { renderLinkWith } from \"../../link\";\nimport { resolveMedia } from \"../../media\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport { seamClasses } from \"../../seam\";\nimport type { TestimonialMasonryBlockData } from \"../../types\";\n\nexport function TestimonialMasonryBlockRenderer({\n block,\n overlapAbove,\n overlapBelow,\n containerClassName,\n linkComponent,\n}: BlockRendererProps<TestimonialMasonryBlockData>) {\n const items: TestimonialItem[] = (block.items ?? [])\n .filter((row) => row.content && row.author?.name)\n .map((row, i) => {\n const avatar = resolveMedia(row.author?.avatar);\n return {\n id: row.id ?? String(i),\n content: row.content as string,\n author: {\n name: row.author?.name as string,\n ...(row.author?.title ? { title: row.author.title } : {}),\n // The library falls back to initials when this is absent, which is\n // why an unresolved upload must not become an empty string.\n ...(avatar ? { avatarUrl: avatar.src } : {}),\n },\n };\n });\n if (items.length === 0) return null;\n\n const link = block.link;\n const hasLink = Boolean(link?.href && link?.label);\n\n return (\n // The band's surface is full-bleed; only its contents sit at the measure.\n <section className={cx(\"bg-accent\", seamClasses({ overlapAbove, overlapBelow }))}>\n <div className={cx(containerClassName, \"py-16 lg:py-24\")}>\n <TestimonialMasonry\n className=\"bg-transparent px-0 lg:px-0\"\n containerClassName=\"max-w-none px-0 py-0 sm:py-0 md:px-0 md:py-0\"\n items={items}\n title={block.title ?? \"\"}\n renderLink={renderLinkWith(linkComponent)}\n {...(block.eyebrow ? { eyebrow: block.eyebrow } : {})}\n {...(block.description ? { description: block.description } : {})}\n {...(typeof block.minItemsForFade === \"number\"\n ? { minItemsForFade: block.minItemsForFade }\n : {})}\n {...(typeof block.maxVisibleRows === \"number\"\n ? { maxVisibleRows: block.maxVisibleRows }\n : {})}\n {...(hasLink && link\n ? {\n link: {\n label: link.label as string,\n href: link.href as string,\n newTab: link.newTab ?? false,\n },\n }\n : {})}\n />\n </div>\n </section>\n );\n}\n","import {\n JsonLd,\n NapBlock,\n type NapDepartment,\n type NapRecord,\n} from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { renderLinkWith } from \"../../link\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport { seamClasses } from \"../../seam\";\nimport type { NapBlockData } from \"../../types\";\n\nexport function NapBlockRenderer({\n block,\n overlapAbove,\n overlapBelow,\n containerClassName,\n linkComponent,\n}: BlockRendererProps<NapBlockData>) {\n const address = block.address;\n const departments: NapDepartment[] = (block.departments ?? [])\n // `phoneE164` is what becomes the `tel:` href and the structured data's\n // `telephone`, so a department without one is not publishable as a NAP\n // entry at all — dropping it beats emitting an unreachable number.\n .filter((row) => row.name && row.phoneE164)\n .map((row, i) => ({\n id: row.id ?? String(i),\n name: row.name as string,\n ...(row.departmentType ? { type: row.departmentType } : {}),\n phone: {\n e164: row.phoneE164 as string,\n ...(row.phoneDisplay ? { display: row.phoneDisplay } : {}),\n },\n }));\n\n if (!block.businessName || !address?.streetAddress || departments.length === 0)\n return null;\n\n const record: NapRecord = {\n name: block.businessName,\n type: block.businessType ?? \"LocalBusiness\",\n address: {\n streetAddress: address.streetAddress,\n addressLocality: address.addressLocality ?? \"\",\n addressRegion: address.addressRegion ?? \"\",\n postalCode: address.postalCode ?? \"\",\n ...(address.addressCountry\n ? { addressCountry: address.addressCountry }\n : {}),\n },\n departments,\n ...(block.url ? { url: block.url } : {}),\n };\n\n return (\n <section className={seamClasses({ overlapAbove, overlapBelow })}>\n <div className={cx(containerClassName, \"py-16 lg:py-24\")}>\n <NapBlock\n record={record}\n showName={block.showName ?? true}\n headingLevel={block.headingLevel ?? \"h2\"}\n renderLink={renderLinkWith(linkComponent)}\n />\n {block.emitJsonLd === false ? null : <JsonLd data={record} />}\n </div>\n </section>\n );\n}\n","import {\n StatsBandBlock,\n type StatItem,\n type StatsBandColumns,\n} from \"@bison-lab/ui\";\n\nimport { cx } from \"../../cx\";\nimport { renderLinkWith } from \"../../link\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport { SEAM_PULL_DOWN, SEAM_PULL_UP, seamClasses } from \"../../seam\";\nimport type { StatsBandBlockData } from \"../../types\";\n\n/** The select only offers 2–6, but a site can edit the config; fall back to the design's four. */\nfunction columnsFrom(value: StatsBandBlockData[\"columns\"]): StatsBandColumns {\n const n = Number(value ?? 4);\n return (Number.isInteger(n) && n >= 2 && n <= 6 ? n : 4) as StatsBandColumns;\n}\n\nexport function StatsBandBlockRenderer({\n block,\n index,\n isLast,\n overlapAbove,\n overlapBelow,\n containerClassName,\n linkComponent,\n}: BlockRendererProps<StatsBandBlockData>) {\n // A stat is its figure: a row without one is dropped rather than rendered\n // as an empty cell, and the band needs two to be a band at all.\n const items: StatItem[] = (block.items ?? []).flatMap((row, i) =>\n row.value\n ? [\n {\n id: row.id ?? String(i),\n value: row.value,\n label: row.label ?? \"\",\n ...(row.href ? { href: row.href } : {}),\n ...(row.wide ? { wide: true } : {}),\n },\n ]\n : [],\n );\n if (items.length < 2) return null;\n\n // Floating only makes sense across a seam: first on the page, the band\n // keeps its top padding, and last on the page it keeps its bottom padding\n // rather than pulling the footer up. The neighbours clear the same\n // distance through `seamClasses` (see `seam.ts`), and so does this band\n // when the neighbour is the one floating: two floating bands in a row\n // would otherwise pull into each other, so on that side neither pulls and\n // both clear. A floating band is headless (the config hides the heading\n // fields), so any heading left over from before `overlap` was ticked is\n // not pulled up into the hero.\n const floats = Boolean(block.overlap);\n const overAbove = floats && index > 0 && !overlapAbove;\n const overBelow = floats && !isLast && !overlapBelow;\n\n return (\n <section\n className={cx(\n \"relative\",\n floats && \"z-10\",\n seamClasses({ overlapAbove, overlapBelow }),\n )}\n >\n <div\n className={cx(\n containerClassName,\n overAbove ? SEAM_PULL_UP : \"pt-16 lg:pt-24\",\n overBelow ? SEAM_PULL_DOWN : \"pb-16 lg:pb-24\",\n )}\n >\n <StatsBandBlock\n items={items}\n columns={columnsFrom(block.columns)}\n tone={block.tone ?? \"card\"}\n align={block.align ?? \"start\"}\n eyebrow={(!floats && block.eyebrow) || undefined}\n title={(!floats && block.title) || undefined}\n description={(!floats && block.description) || undefined}\n containerClassName=\"max-w-none px-0 sm:px-0\"\n renderLink={renderLinkWith(linkComponent)}\n />\n </div>\n </section>\n );\n}\n","/**\n * The one rule for a typed panel width, shared by the config's validator and\n * the renderer's reader so the two cannot drift: if the validator accepted a\n * value the renderer dropped, the editor would be back to a width that\n * silently does nothing. React-free, because `config.ts` ships from the Node\n * entry.\n */\nconst REM_WIDTH = /^\\s*(\\d+(?:\\.\\d+)?)\\s*(rem)?\\s*$/;\n\n/** \"30\" and \"30rem\" both mean 30rem; anything else is not a width. */\nexport function remWidth(\n value: string | null | undefined,\n): `${number}rem` | null {\n const match = REM_WIDTH.exec(value ?? \"\");\n return match ? (`${Number(match[1])}rem` as `${number}rem`) : null;\n}\n","import {\n MegaMenuPanel,\n type MegaMenuColumn,\n type MegaMenuIcons,\n type MegaMenuLink,\n type MegaMenuPanelData,\n type MegaMenuSection,\n type MegaMenuVariants,\n type MegaMenuWidth,\n} from \"@bison-lab/ui\";\n\nimport { renderLinkWith } from \"../../link\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport type { MegaMenuBlockData, MegaMenuLinkData } from \"../../types\";\nimport { remWidth } from \"./rem-width\";\n\nfunction completeLink(row: MegaMenuLinkData): MegaMenuLink | null {\n if (!row.label || !row.href) return null;\n return {\n label: row.label,\n href: row.href,\n ...(row.description ? { description: row.description } : {}),\n ...(row.variant ? { variant: row.variant } : {}),\n ...(row.icon ? { icon: row.icon } : {}),\n ...(row.newTab ? { newTab: true } : {}),\n };\n}\n\n/**\n * The row as `MegaMenuPanel` data, or `null` when there is nothing to open:\n * no columns, or columns whose links are not yet complete. Draft saves skip\n * validation, so a live preview genuinely hands this half-built rows.\n */\nexport function megaMenuPanelFromRow(\n row: MegaMenuBlockData,\n): MegaMenuPanelData | null {\n const panel = row.panel;\n if (!panel?.columns?.length) return null;\n const custom = Boolean(panel.customWidths);\n\n const columns: MegaMenuColumn[] = [];\n for (const column of panel.columns) {\n const sections: MegaMenuSection[] = [];\n for (const section of column.sections ?? []) {\n const links = (section.links ?? [])\n .map(completeLink)\n .filter((link): link is MegaMenuLink => link !== null);\n if (!links.length) continue;\n sections.push({\n ...(section.eyebrow ? { eyebrow: section.eyebrow } : {}),\n display: section.display ?? \"list\",\n hideDescriptionsOnMobile: section.hideDescriptionsOnMobile ?? true,\n links,\n });\n }\n columns.push({\n ...(custom && column.customWidth\n ? { customWidth: column.customWidth }\n : { width: Number(column.width ?? \"100\") as MegaMenuWidth }),\n ...(column.divider ? { divider: true } : {}),\n sections,\n });\n }\n if (!columns.some((column) => column.sections.length)) return null;\n\n const overview = panel.footer?.overview;\n const cta = panel.footer?.cta;\n // Independent of the column checkbox: the config gates only `customWidth`\n // on it, and the help text promises a typed panel width is used.\n const customMax = remWidth(panel.customMaxWidth);\n\n return {\n maxWidth: customMax ?? panel.maxWidth ?? \"standard\",\n columns,\n footer: {\n ...(overview?.label && overview.href\n ? {\n overview: {\n label: overview.label,\n href: overview.href,\n ...(overview.newTab ? { newTab: true } : {}),\n },\n }\n : {}),\n ...(cta?.label && cta.href\n ? {\n cta: {\n label: cta.label,\n href: cta.href,\n ...(cta.newTab ? { newTab: true } : {}),\n },\n }\n : {}),\n },\n };\n}\n\nexport interface MegaMenuBlockRendererProps\n extends BlockRendererProps<MegaMenuBlockData> {\n /** The look behind each `variants` value the site passed to `megaMenuBlock`. */\n variants?: MegaMenuVariants;\n /** The glyph behind each `icons` value the site passed to `megaMenuBlock`. */\n icons?: MegaMenuIcons;\n}\n\n/**\n * One panel, standing alone: what a live preview shows while the row is being\n * edited. A whole header goes through `headerItemsFromBlocks` instead.\n */\nexport function MegaMenuBlockRenderer({\n block,\n linkComponent,\n variants,\n icons,\n}: MegaMenuBlockRendererProps) {\n const panel = megaMenuPanelFromRow(block);\n if (!panel) return null;\n return (\n <MegaMenuPanel\n {...panel}\n variants={variants}\n icons={icons}\n renderLink={renderLinkWith(linkComponent)}\n />\n );\n}\n","import {\n megaMenuToFloatingNavItems,\n type FloatingNavItem,\n type MegaMenuIcons,\n type MegaMenuItem,\n type MegaMenuVariants,\n} from \"@bison-lab/ui\";\n\nimport { renderLinkWith, type BlockLinkComponent } from \"../../link\";\nimport type { MegaMenuBlockData, NavLinkBlockData } from \"../../types\";\nimport { megaMenuPanelFromRow } from \"./component\";\n\n/** A row of a header global's `items`: either block, or whatever else a site added. */\nexport type HeaderBlockRow = MegaMenuBlockData | NavLinkBlockData;\n\nexport interface HeaderItemsOptions {\n variants?: MegaMenuVariants;\n icons?: MegaMenuIcons;\n /** Marks the current section; receives each item's `href`. */\n isActive?: (href: string) => boolean;\n /** The site's link adapter, applied to every link inside the panels. */\n linkComponent?: BlockLinkComponent;\n}\n\n/**\n * A global's `items` as `FloatingNavBlock` items, so a site's header is one\n * call. Rows the bar cannot show are dropped rather than rendered broken: a\n * row with no label, a mega menu with nothing to open, and any block type a\n * site added that this package does not know.\n *\n * A `navLink` row's `newTab` is not carried: `FloatingNavItem` has no such\n * field, because the bar renders its own links.\n */\nexport function headerItemsFromBlocks(\n blocks: HeaderBlockRow[],\n { variants, icons, isActive, linkComponent }: HeaderItemsOptions = {},\n): FloatingNavItem[] {\n const items: MegaMenuItem[] = [];\n for (const row of blocks) {\n if (!row.label) continue;\n if (row.blockType === \"navLink\") {\n if (!row.href) continue;\n items.push({ label: row.label, href: row.href });\n } else if (row.blockType === \"megaMenu\") {\n const panel = megaMenuPanelFromRow(row);\n if (!panel) continue;\n items.push({\n label: row.label,\n ...(row.href ? { href: row.href } : {}),\n panel,\n });\n }\n }\n return megaMenuToFloatingNavItems(\n { items },\n {\n isActive,\n variants,\n icons,\n ...(linkComponent ? { renderLink: renderLinkWith(linkComponent) } : {}),\n },\n );\n}\n"],"mappings":"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"}
|
package/dist/rich-text.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
2
|
+
import { p as BlockRendererProps, s as RichTextBlockData } from "./types-D9rg0BoW.mjs";
|
|
3
3
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/blocks/rich-text/component.d.ts
|
|
@@ -17,6 +17,8 @@ import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
|
17
17
|
*/
|
|
18
18
|
declare function RichTextBlockRenderer({
|
|
19
19
|
block,
|
|
20
|
+
overlapAbove,
|
|
21
|
+
overlapBelow,
|
|
20
22
|
containerClassName
|
|
21
23
|
}: BlockRendererProps<RichTextBlockData>): react_jsx_runtime0.JSX.Element | null;
|
|
22
24
|
//#endregion
|
package/dist/rich-text.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-text.d.mts","names":[],"sources":["../src/blocks/rich-text/component.tsx"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"rich-text.d.mts","names":[],"sources":["../src/blocks/rich-text/component.tsx"],"mappings":";;;;;;;AAoBA;;;;;;;;;;iBAAgB,qBAAA,CAAA;EACd,KAAA;EACA,YAAA;EACA,YAAA;EACA;AAAA,GACC,kBAAA,CAAmB,iBAAA,IAAkB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
|
package/dist/rich-text.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { o as seamClasses, s as cx } from "./seam-BDFZlV3F.mjs";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
import { RichText } from "@payloadcms/richtext-lexical/react";
|
|
5
5
|
//#region src/blocks/rich-text/component.tsx
|
|
@@ -15,13 +15,19 @@ import { RichText } from "@payloadcms/richtext-lexical/react";
|
|
|
15
15
|
* generated block type matches it, and Lexical's own type is the same shape
|
|
16
16
|
* with tighter unions.
|
|
17
17
|
*/
|
|
18
|
-
function RichTextBlockRenderer({ block, containerClassName }) {
|
|
18
|
+
function RichTextBlockRenderer({ block, overlapAbove, overlapBelow, containerClassName }) {
|
|
19
19
|
if (!block.content) return null;
|
|
20
20
|
return /* @__PURE__ */ jsx("section", {
|
|
21
|
-
className:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
className: seamClasses({
|
|
22
|
+
overlapAbove,
|
|
23
|
+
overlapBelow
|
|
24
|
+
}),
|
|
25
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
26
|
+
className: cx(containerClassName, "py-16 lg:py-20"),
|
|
27
|
+
children: /* @__PURE__ */ jsx(RichText, {
|
|
28
|
+
data: block.content,
|
|
29
|
+
className: "max-w-prose"
|
|
30
|
+
})
|
|
25
31
|
})
|
|
26
32
|
});
|
|
27
33
|
}
|
package/dist/rich-text.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-text.mjs","names":[],"sources":["../src/blocks/rich-text/component.tsx"],"sourcesContent":["import { RichText } from \"@payloadcms/richtext-lexical/react\";\nimport type { SerializedEditorState } from \"@payloadcms/richtext-lexical/lexical\";\n\nimport { cx } from \"../../cx\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport type { RichTextBlockData } from \"../../types\";\n\n/**\n * A Lexical document as prose.\n *\n * This is the one renderer that needs `@payloadcms/richtext-lexical`, which is\n * why it ships from its own entry (`@bison-lab/payload-blocks/rich-text`)\n * rather than the `./react` barrel every consumer loads.\n *\n * The cast at the boundary is the price of the package's no-generated-types\n * rule: `RichTextContent` describes the document structurally so a site's\n * generated block type matches it, and Lexical's own type is the same shape\n * with tighter unions.\n */\nexport function RichTextBlockRenderer({\n block,\n containerClassName,\n}: BlockRendererProps<RichTextBlockData>) {\n if (!block.content) return null;\n return (\n <section className={cx(containerClassName, \"py-16 lg:py-20\")}>\n
|
|
1
|
+
{"version":3,"file":"rich-text.mjs","names":[],"sources":["../src/blocks/rich-text/component.tsx"],"sourcesContent":["import { RichText } from \"@payloadcms/richtext-lexical/react\";\nimport type { SerializedEditorState } from \"@payloadcms/richtext-lexical/lexical\";\n\nimport { cx } from \"../../cx\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport { seamClasses } from \"../../seam\";\nimport type { RichTextBlockData } from \"../../types\";\n\n/**\n * A Lexical document as prose.\n *\n * This is the one renderer that needs `@payloadcms/richtext-lexical`, which is\n * why it ships from its own entry (`@bison-lab/payload-blocks/rich-text`)\n * rather than the `./react` barrel every consumer loads.\n *\n * The cast at the boundary is the price of the package's no-generated-types\n * rule: `RichTextContent` describes the document structurally so a site's\n * generated block type matches it, and Lexical's own type is the same shape\n * with tighter unions.\n */\nexport function RichTextBlockRenderer({\n block,\n overlapAbove,\n overlapBelow,\n containerClassName,\n}: BlockRendererProps<RichTextBlockData>) {\n if (!block.content) return null;\n return (\n <section className={seamClasses({ overlapAbove, overlapBelow })}>\n <div className={cx(containerClassName, \"py-16 lg:py-20\")}>\n <RichText\n data={block.content as unknown as SerializedEditorState}\n className=\"max-w-prose\"\n />\n </div>\n </section>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,SAAgB,sBAAsB,EACpC,OACA,cACA,cACA,sBACwC;AACxC,KAAI,CAAC,MAAM,QAAS,QAAO;AAC3B,QACE,oBAAC,WAAD;EAAS,WAAW,YAAY;GAAE;GAAc;GAAc,CAAC;YAC7D,oBAAC,OAAD;GAAK,WAAW,GAAG,oBAAoB,iBAAiB;aACtD,oBAAC,UAAD;IACE,MAAM,MAAM;IACZ,WAAU;IACV,CAAA;GACE,CAAA;EACE,CAAA"}
|