@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/dist/react.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
- import { t as cx } from "./cx-BKHSv3xT.mjs";
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: cx(containerClassName, "py-16 lg:py-24"),
225
- children: /* @__PURE__ */ jsx(ShowcasePanelsBlock, {
226
- items,
227
- spineVariant: block.spineVariant ?? "numbered",
228
- defaultActiveIndex: Math.min(Math.max(block.defaultActiveIndex ?? 0, 0), items.length - 1),
229
- ...block.watermark ? { watermark: block.watermark } : {},
230
- renderLink: renderLinkWith(linkComponent),
231
- renderImage: (item, i) => /* @__PURE__ */ jsx(Image, {
232
- src: item.image.src,
233
- alt: item.image.alt ?? "",
234
- width: dims[i]?.width,
235
- height: dims[i]?.height,
236
- className: "absolute inset-0 h-full w-full object-cover",
237
- sizes: "(min-width: 1024px) 50vw, 100vw"
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: cx(containerClassName, "py-16 lg:py-24"),
264
- children: /* @__PURE__ */ jsx(ProcessStepsBlock, {
265
- items,
266
- autoAdvance: block.autoAdvance ?? true,
267
- autoAdvanceDuration: block.autoAdvanceDuration ?? 6e3,
268
- pauseOnHover: block.pauseOnHover ?? true,
269
- defaultActiveIndex: Math.min(Math.max(block.defaultActiveIndex ?? 0, 0), items.length - 1),
270
- renderImage: (item, i) => /* @__PURE__ */ jsx(Image, {
271
- src: item.image.src,
272
- alt: item.image.alt ?? "",
273
- width: dims[i]?.width,
274
- height: dims[i]?.height,
275
- className: "size-full object-cover",
276
- sizes: "(min-width: 768px) 66vw, 100vw"
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", { children: /* @__PURE__ */ jsx("div", {
296
- className: cx(containerClassName, "py-16 lg:py-24"),
297
- children: /* @__PURE__ */ jsx(FAQColumnsBlock, {
298
- className: "py-0 md:py-0",
299
- containerClassName: "max-w-none px-0 sm:px-0",
300
- items,
301
- title: block.title ?? "",
302
- ...block.eyebrow ? { eyebrow: block.eyebrow } : {},
303
- ...block.description ? { description: block.description } : {},
304
- sticky: block.sticky ?? true,
305
- type: block.type ?? "single",
306
- collapsible: block.collapsible ?? true,
307
- renderLink: renderLinkWith(linkComponent),
308
- ...hasCta && cta ? { cta: {
309
- title: cta.title ?? "",
310
- linkText: cta.linkText,
311
- href: cta.href,
312
- newTab: cta.newTab ?? false
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", { children: /* @__PURE__ */ jsxs("div", {
386
- className: cx(containerClassName, "py-16 lg:py-24"),
387
- children: [/* @__PURE__ */ jsx(NapBlock, {
388
- record,
389
- showName: block.showName ?? true,
390
- headingLevel: block.headingLevel ?? "h2",
391
- renderLink: renderLinkWith(linkComponent)
392
- }), block.emitJsonLd === false ? null : /* @__PURE__ */ jsx(JsonLd, { data: record })]
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
@@ -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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"}
@@ -1,5 +1,5 @@
1
1
 
2
- import { f as BlockRendererProps, s as RichTextBlockData } from "./types-CJAvoZMk.mjs";
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
@@ -1 +1 @@
1
- {"version":3,"file":"rich-text.d.mts","names":[],"sources":["../src/blocks/rich-text/component.tsx"],"mappings":";;;;;;;AAmBA;;;;;;;;;;iBAAgB,qBAAA,CAAA;EACd,KAAA;EACA;AAAA,GACC,kBAAA,CAAmB,iBAAA,IAAkB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
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"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { t as cx } from "./cx-BKHSv3xT.mjs";
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: cx(containerClassName, "py-16 lg:py-20"),
22
- children: /* @__PURE__ */ jsx(RichText, {
23
- data: block.content,
24
- className: "max-w-prose"
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
  }
@@ -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 <RichText\n data={block.content as unknown as SerializedEditorState}\n className=\"max-w-prose\"\n />\n </section>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmBA,SAAgB,sBAAsB,EACpC,OACA,sBACwC;AACxC,KAAI,CAAC,MAAM,QAAS,QAAO;AAC3B,QACE,oBAAC,WAAD;EAAS,WAAW,GAAG,oBAAoB,iBAAiB;YAC1D,oBAAC,UAAD;GACE,MAAM,MAAM;GACZ,WAAU;GACV,CAAA;EACM,CAAA"}
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"}