@bison-lab/payload-blocks 3.11.0 → 3.21.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.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
- import { A as BlockImageProps, C as resolveLink, D as newTabAttributes, E as DefaultBlockLink, O as renderLinkWith, S as linkTypeOf, T as BlockLinkProps, _ as RenderBlocksProps, a as NavLinkBlockData, b as LinkValue, c as ShowcasePanelsBlockData, d as BLOCK_ID_ATTRIBUTE, f as BlockLike, g as RenderBlocks, h as DEFAULT_CONTAINER, i as NapBlockData, j as DefaultBlockImage, k as BlockImageComponent, l as StatsBandBlockData, m as BlockRendererProps, n as HeroBlockData, o as ProcessStepsBlockData, p as BlockRegistryFor, r as MegaMenuBlockData, t as FaqColumnsBlockData, u as TestimonialMasonryBlockData, v as LinkDestination, w as BlockLinkComponent, x as ResolveLink, y as LinkPageDoc } from "./types-ODMRyOpH.mjs";
2
+ import { A as BlockImageComponent, C as linkTypeOf, D as DefaultBlockLink, E as BlockLinkProps, M as DefaultBlockImage, O as newTabAttributes, S as ResolveLink, T as BlockLinkComponent, _ as RenderBlocks, a as NavLinkBlockData, b as LinkPageDoc, d as TestimonialMasonryBlockData, f as BLOCK_ID_ATTRIBUTE, g as DEFAULT_CONTAINER, h as BlockRendererProps, i as NapBlockData, j as BlockImageProps, k as renderLinkWith, l as ShowcasePanelsBlockData, m as BlockRegistryFor, n as HeroBlockData, o as NavigationSettingsData, p as BlockLike, r as MegaMenuBlockData, s as ProcessStepsBlockData, t as FaqColumnsBlockData, u as StatsBandBlockData, v as RenderBlocksProps, w as resolveLink, x as LinkValue, y as LinkDestination } from "./types-BurBx5me.mjs";
3
3
  import * as react_jsx_runtime0 from "react/jsx-runtime";
4
- import { FloatingNavItem, MegaMenuIcons, MegaMenuPanelData, MegaMenuVariants } from "@bison-lab/ui";
4
+ import { FloatingNavItem, MegaMenuIcons, MegaMenuMaxWidth, MegaMenuPanelData, MegaMenuVariants } from "@bison-lab/ui";
5
5
 
6
6
  //#region src/seam.d.ts
7
7
  /**
@@ -125,7 +125,7 @@ declare function StatsBandBlockRenderer({
125
125
  * `resolve` turns each link's page into a path; the package's own is the
126
126
  * default.
127
127
  */
128
- declare function megaMenuPanelFromRow(row: MegaMenuBlockData, resolve?: ResolveLink): MegaMenuPanelData | null;
128
+ declare function megaMenuPanelFromRow(row: MegaMenuBlockData, resolve?: ResolveLink, defaultMaxWidth?: MegaMenuMaxWidth): MegaMenuPanelData | null;
129
129
  interface MegaMenuBlockRendererProps extends BlockRendererProps<MegaMenuBlockData> {
130
130
  /** The look behind each `variants` value the site passed to `megaMenuBlock`. */
131
131
  variants?: MegaMenuVariants;
@@ -156,6 +156,35 @@ interface HeaderItemsOptions {
156
156
  linkComponent?: BlockLinkComponent;
157
157
  /** The site's resolver for page links; defaults to the package's `resolveLink`. */
158
158
  resolveLink?: ResolveLink;
159
+ /** Fallback panel width when a mega menu row does not set `panel.maxWidth`. */
160
+ defaultMaxWidth?: MegaMenuMaxWidth;
161
+ }
162
+ /** The Header global (or `getNavigation`'s `{ header, bar }`) as this helper reads it. */
163
+ interface HeaderNavigationDoc {
164
+ header?: {
165
+ items?: unknown;
166
+ } | null;
167
+ bar?: NavigationSettingsData | null;
168
+ }
169
+ interface HeaderFromNavigationOptions {
170
+ icons?: MegaMenuIcons;
171
+ isActive?: (href: string) => boolean;
172
+ linkComponent?: BlockLinkComponent;
173
+ /**
174
+ * Featured-link looks from chrome roles (`megaMenuVariantsFromLooks`),
175
+ * not a variants array on Header.
176
+ */
177
+ looks?: MegaMenuVariants;
178
+ resolveLink?: ResolveLink;
179
+ /** Holds the named item's panel open — a CMS live preview of the edited row. */
180
+ openItem?: string;
181
+ }
182
+ interface HeaderFromNavigationProps {
183
+ items: FloatingNavItem[];
184
+ breakpoint: "lg";
185
+ hideOnScroll: boolean;
186
+ viewport: boolean;
187
+ openItem?: string;
159
188
  }
160
189
  /**
161
190
  * A global's `items` as `FloatingNavBlock` items, so a site's header is one
@@ -165,16 +194,32 @@ interface HeaderItemsOptions {
165
194
  *
166
195
  * A `navLink` row's `newTab` is not carried: `FloatingNavItem` has no such
167
196
  * field, because the bar renders its own links. A bar item whose page is
168
- * missing or unpublished is dropped, like a panel link. A mega menu's own
169
- * `href` (the trigger's landing page) is plain text, not a picked page.
197
+ * missing or unpublished is dropped, like a panel link. A mega menu's
198
+ * landing page goes through the same resolver: unpublished or missing
199
+ * pages omit the trigger `href` and keep the panel.
170
200
  */
171
201
  declare function headerItemsFromBlocks(blocks: HeaderBlockRow[], {
172
202
  variants,
173
203
  icons,
174
204
  isActive,
175
205
  linkComponent,
176
- resolveLink: resolve
206
+ resolveLink: resolve,
207
+ defaultMaxWidth
177
208
  }?: HeaderItemsOptions): FloatingNavItem[];
209
+ /**
210
+ * The Header global as `FloatingNavBlock` props: items through
211
+ * `headerItemsFromBlocks`, `breakpoint: "lg"`, and bar behaviour from
212
+ * Settings. Looks come from chrome roles (`looks`), not a Header array.
213
+ * A site without the consume bump (SPI-99) never reads these fields.
214
+ */
215
+ declare function headerFromNavigation(doc: HeaderNavigationDoc, {
216
+ icons,
217
+ isActive,
218
+ linkComponent,
219
+ looks,
220
+ resolveLink: resolve,
221
+ openItem
222
+ }?: HeaderFromNavigationOptions): HeaderFromNavigationProps;
178
223
  //#endregion
179
- export { BLOCK_ID_ATTRIBUTE, type BlockImageComponent, type BlockImageProps, type BlockLike, type BlockLinkComponent, type BlockLinkProps, type BlockRegistryFor, type BlockRendererProps, DEFAULT_CONTAINER, DefaultBlockImage, DefaultBlockLink, FaqColumnsBlockRenderer, type HeaderBlockRow, type HeaderItemsOptions, HeroBlockRenderer, type LinkDestination, type LinkPageDoc, type LinkValue, MegaMenuBlockRenderer, type MegaMenuBlockRendererProps, NapBlockRenderer, OVERLAP_ABOVE_CLEARANCE, OVERLAP_BELOW_CLEARANCE, ProcessStepsBlockRenderer, RenderBlocks, type RenderBlocksProps, type ResolveLink, SEAM_PULL_DOWN, SEAM_PULL_UP, ShowcasePanelsBlockRenderer, StatsBandBlockRenderer, TestimonialMasonryBlockRenderer, headerItemsFromBlocks, linkTypeOf, megaMenuPanelFromRow, newTabAttributes, overlapsSeam, renderLinkWith, resolveLink, seamClasses };
224
+ export { BLOCK_ID_ATTRIBUTE, type BlockImageComponent, type BlockImageProps, type BlockLike, type BlockLinkComponent, type BlockLinkProps, type BlockRegistryFor, type BlockRendererProps, DEFAULT_CONTAINER, DefaultBlockImage, DefaultBlockLink, FaqColumnsBlockRenderer, type HeaderBlockRow, type HeaderFromNavigationOptions, type HeaderFromNavigationProps, type HeaderItemsOptions, type HeaderNavigationDoc, HeroBlockRenderer, type LinkDestination, type LinkPageDoc, type LinkValue, MegaMenuBlockRenderer, type MegaMenuBlockRendererProps, NapBlockRenderer, OVERLAP_ABOVE_CLEARANCE, OVERLAP_BELOW_CLEARANCE, ProcessStepsBlockRenderer, RenderBlocks, type RenderBlocksProps, type ResolveLink, SEAM_PULL_DOWN, SEAM_PULL_UP, ShowcasePanelsBlockRenderer, StatsBandBlockRenderer, TestimonialMasonryBlockRenderer, headerFromNavigation, headerItemsFromBlocks, linkTypeOf, megaMenuPanelFromRow, newTabAttributes, overlapsSeam, renderLinkWith, resolveLink, seamClasses };
180
225
  //# sourceMappingURL=react.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.mts","names":[],"sources":["../src/seam.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/component.tsx","../src/blocks/mega-menu/header.tsx"],"mappings":";;;;;;;;;;AAqBA;;;;;AAGA;;;;;AAGA;;cANa,YAAA;;cAGA,cAAA;AAMb;AAAA,cAHa,uBAAA;;cAGA,uBAAA;;AAOb;;;;iBAAgB,YAAA,CAAa,KAAA,EAAO,SAAA;AAQpC;AAAA,iBAAgB,WAAA,CAAA;EACd,YAAA;EACA;AAAA,GACC,IAAA,CAAK,kBAAA;;;;;;AA3BR;;;;iBCKgB,iBAAA,CAAA;EACd,KAAA;EACA,KAAA;EACA,YAAA;EACA,YAAA;EACA,kBAAA;EACA,cAAA,EAAgB,KAAA;EAChB,aAAA,EAAe,IAAA;EACf,WAAA,EAAa;AAAA,GACZ,kBAAA,CAAmB,aAAA,IAAc,kBAAA,CAAA,GAAA,CAAA,OAAA;;;iBC1BpB,2BAAA,CAAA;EACd,KAAA;EACA,YAAA;EACA,YAAA;EACA,kBAAA;EACA,cAAA,EAAgB,KAAA;EAChB;AAAA,GACC,kBAAA,CAAmB,uBAAA,IAAwB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;iBCR9B,yBAAA,CAAA;EACd,KAAA;EACA,YAAA;EACA,YAAA;EACA,kBAAA;EACA,cAAA,EAAgB;AAAA,GACf,kBAAA,CAAmB,qBAAA,IAAsB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;iBCN5B,uBAAA,CAAA;EACd,KAAA;EACA,YAAA;EACA,YAAA;EACA,kBAAA;EACA,aAAA;EACA,WAAA,EAAa;AAAA,GACZ,kBAAA,CAAmB,mBAAA,IAAoB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;iBCN1B,+BAAA,CAAA;EACd,KAAA;EACA,YAAA;EACA,YAAA;EACA,kBAAA;EACA,aAAA;EACA,WAAA,EAAa;AAAA,GACZ,kBAAA,CAAmB,2BAAA,IAA4B,kBAAA,CAAA,GAAA,CAAA,OAAA;;;iBCHlC,gBAAA,CAAA;EACd,KAAA;EACA,YAAA;EACA,YAAA;EACA,kBAAA;EACA;AAAA,GACC,kBAAA,CAAmB,YAAA,IAAa,kBAAA,CAAA,GAAA,CAAA,OAAA;;;iBCDnB,sBAAA,CAAA;EACd,KAAA;EACA,KAAA;EACA,MAAA;EACA,YAAA;EACA,YAAA;EACA,kBAAA;EACA;AAAA,GACC,kBAAA,CAAmB,kBAAA,IAAmB,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;APLzC;;;;;AAGA;iBQqBgB,oBAAA,CACd,GAAA,EAAK,iBAAA,EACL,OAAA,GAAS,WAAA,GACR,iBAAA;AAAA,UA4Dc,0BAAA,SACP,kBAAA,CAAmB,iBAAA;ERrFF;EQuFzB,QAAA,GAAW,gBAAA;ERpFA;EQsFX,KAAA,GAAQ,aAAA;AAAA;;;ARnFV;;iBQ0FgB,qBAAA,CAAA;EACd,KAAA;EACA,aAAA;EACA,WAAA,EAAa,OAAA;EACb,QAAA;EACA;AAAA,GACC,0BAAA,GAA0B,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;KChHjB,cAAA,GAAiB,iBAAA,GAAoB,gBAAA;AAAA,UAEhC,kBAAA;EACf,QAAA,GAAW,gBAAA;EACX,KAAA,GAAQ,aAAA;ETGe;ESDvB,QAAA,IAAY,IAAA;ETIa;ESFzB,aAAA,GAAgB,kBAAA;ETES;ESAzB,WAAA,GAAc,WAAA;AAAA;;;;;ATMhB;;;;;AAOA;;iBSCgB,qBAAA,CACd,MAAA,EAAQ,cAAA;EAEN,QAAA;EACA,KAAA;EACA,QAAA;EACA,aAAA;EACA,WAAA,EAAa;AAAA,IACZ,kBAAA,GACF,eAAA"}
1
+ {"version":3,"file":"react.d.mts","names":[],"sources":["../src/seam.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/component.tsx","../src/blocks/mega-menu/header.tsx"],"mappings":"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"}
package/dist/react.mjs CHANGED
@@ -1,8 +1,9 @@
1
1
  "use client";
2
- import { a as overlapsSeam, c as DefaultBlockLink, i as SEAM_PULL_UP, l as newTabAttributes, n as OVERLAP_BELOW_CLEARANCE, o as seamClasses, r as SEAM_PULL_DOWN, s as cx, t as OVERLAP_ABOVE_CLEARANCE, u as renderLinkWith } from "./seam-CmLb3BSo.mjs";
3
- import { n as resolveLink, t as linkTypeOf } from "./link-CnwLtm47.mjs";
2
+ import { n as newTabAttributes, r as renderLinkWith, t as DefaultBlockLink } from "./link-32261jXB.mjs";
3
+ 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-Cz7oiQ5z.mjs";
4
+ import { a as linkTypeOf, i as megaMenuPanelFromRow, n as headerItemsFromBlocks, r as MegaMenuBlockRenderer, s as resolveLink, t as headerFromNavigation } from "./header-CV7K-3yB.mjs";
4
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
- import { Button, FAQColumnsBlock, JsonLd, MegaMenuPanel, NapBlock, ProcessStepsBlock, ShowcasePanelsBlock, StatsBandBlock, TestimonialMasonry, megaMenuToFloatingNavItems } from "@bison-lab/ui";
6
+ import { Button, FAQColumnsBlock, JsonLd, NapBlock, ProcessStepsBlock, ShowcasePanelsBlock, StatsBandBlock, TestimonialMasonry } from "@bison-lab/ui";
6
7
  //#region src/image.tsx
7
8
  /**
8
9
  * The fallback when no `imageComponent` is supplied: a plain `<img>`. Correct
@@ -456,147 +457,6 @@ function StatsBandBlockRenderer({ block, index, isLast, overlapAbove, overlapBel
456
457
  });
457
458
  }
458
459
  //#endregion
459
- //#region src/blocks/mega-menu/rem-width.ts
460
- /**
461
- * The one rule for a typed panel width, shared by the config's validator and
462
- * the renderer's reader so the two cannot drift: if the validator accepted a
463
- * value the renderer dropped, the editor would be back to a width that
464
- * silently does nothing. React-free, because `config.ts` ships from the Node
465
- * entry.
466
- */
467
- const REM_WIDTH = /^\s*(\d+(?:\.\d+)?)\s*(rem)?\s*$/;
468
- /** "30" and "30rem" both mean 30rem; anything else is not a width. */
469
- function remWidth(value) {
470
- const match = REM_WIDTH.exec(value ?? "");
471
- return match ? `${Number(match[1])}rem` : null;
472
- }
473
- //#endregion
474
- //#region src/blocks/mega-menu/component.tsx
475
- /**
476
- * A menu link needs both halves. Unlike a call to action, a link whose page
477
- * is missing or unpublished is dropped rather than shown as text: a menu
478
- * item that goes nowhere is worse than one fewer item.
479
- */
480
- function completeLink(row, resolve) {
481
- const href = resolve(row);
482
- if (!row.label || !href) return null;
483
- return {
484
- label: row.label,
485
- href,
486
- ...row.description ? { description: row.description } : {},
487
- ...row.variant ? { variant: row.variant } : {},
488
- ...row.icon ? { icon: row.icon } : {},
489
- ...row.newTab ? { newTab: true } : {}
490
- };
491
- }
492
- /**
493
- * The row as `MegaMenuPanel` data, or `null` when there is nothing to open:
494
- * no columns, or columns whose links are not yet complete. Draft saves skip
495
- * validation, so a live preview genuinely hands this half-built rows.
496
- * `resolve` turns each link's page into a path; the package's own is the
497
- * default.
498
- */
499
- function megaMenuPanelFromRow(row, resolve = resolveLink) {
500
- const panel = row.panel;
501
- if (!panel?.columns?.length) return null;
502
- const custom = Boolean(panel.customWidths);
503
- const columns = [];
504
- for (const column of panel.columns) {
505
- const sections = [];
506
- for (const section of column.sections ?? []) {
507
- const links = (section.links ?? []).map((link) => completeLink(link, resolve)).filter((link) => link !== null);
508
- if (!links.length) continue;
509
- sections.push({
510
- ...section.eyebrow ? { eyebrow: section.eyebrow } : {},
511
- display: section.display ?? "list",
512
- hideDescriptionsOnMobile: section.hideDescriptionsOnMobile ?? true,
513
- links
514
- });
515
- }
516
- columns.push({
517
- ...custom && column.customWidth ? { customWidth: column.customWidth } : { width: Number(column.width ?? "100") },
518
- ...column.divider ? { divider: true } : {},
519
- sections
520
- });
521
- }
522
- if (!columns.some((column) => column.sections.length)) return null;
523
- const overview = footerLink(panel.footer?.overview, resolve);
524
- const cta = footerLink(panel.footer?.cta, resolve);
525
- return {
526
- maxWidth: remWidth(panel.customMaxWidth) ?? panel.maxWidth ?? "standard",
527
- columns,
528
- footer: {
529
- ...overview ? { overview } : {},
530
- ...cta ? { cta } : {}
531
- }
532
- };
533
- }
534
- /** A footer link, complete, or nothing: same rule as `completeLink`. */
535
- function footerLink(row, resolve) {
536
- const href = row ? resolve(row) : null;
537
- if (!row?.label || !href) return null;
538
- return {
539
- label: row.label,
540
- href,
541
- ...row.newTab ? { newTab: true } : {}
542
- };
543
- }
544
- /**
545
- * One panel, standing alone: what a live preview shows while the row is being
546
- * edited. A whole header goes through `headerItemsFromBlocks` instead.
547
- */
548
- function MegaMenuBlockRenderer({ block, linkComponent, resolveLink: resolve, variants, icons }) {
549
- const panel = megaMenuPanelFromRow(block, resolve);
550
- if (!panel) return null;
551
- return /* @__PURE__ */ jsx(MegaMenuPanel, {
552
- ...panel,
553
- variants,
554
- icons,
555
- renderLink: renderLinkWith(linkComponent)
556
- });
557
- }
558
- //#endregion
559
- //#region src/blocks/mega-menu/header.tsx
560
- /**
561
- * A global's `items` as `FloatingNavBlock` items, so a site's header is one
562
- * call. Rows the bar cannot show are dropped rather than rendered broken: a
563
- * row with no label, a mega menu with nothing to open, and any block type a
564
- * site added that this package does not know.
565
- *
566
- * A `navLink` row's `newTab` is not carried: `FloatingNavItem` has no such
567
- * field, because the bar renders its own links. A bar item whose page is
568
- * missing or unpublished is dropped, like a panel link. A mega menu's own
569
- * `href` (the trigger's landing page) is plain text, not a picked page.
570
- */
571
- function headerItemsFromBlocks(blocks, { variants, icons, isActive, linkComponent, resolveLink: resolve = resolveLink } = {}) {
572
- const items = [];
573
- for (const row of blocks) {
574
- if (!row.label) continue;
575
- if (row.blockType === "navLink") {
576
- const href = resolve(row);
577
- if (!href) continue;
578
- items.push({
579
- label: row.label,
580
- href
581
- });
582
- } else if (row.blockType === "megaMenu") {
583
- const panel = megaMenuPanelFromRow(row, resolve);
584
- if (!panel) continue;
585
- items.push({
586
- label: row.label,
587
- ...row.href ? { href: row.href } : {},
588
- panel
589
- });
590
- }
591
- }
592
- return megaMenuToFloatingNavItems({ items }, {
593
- isActive,
594
- variants,
595
- icons,
596
- ...linkComponent ? { renderLink: renderLinkWith(linkComponent) } : {}
597
- });
598
- }
599
- //#endregion
600
- export { BLOCK_ID_ATTRIBUTE, 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, linkTypeOf, megaMenuPanelFromRow, newTabAttributes, overlapsSeam, renderLinkWith, resolveLink, seamClasses };
460
+ export { BLOCK_ID_ATTRIBUTE, 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, headerFromNavigation, headerItemsFromBlocks, linkTypeOf, megaMenuPanelFromRow, newTabAttributes, overlapsSeam, renderLinkWith, resolveLink, seamClasses };
601
461
 
602
462
  //# sourceMappingURL=react.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.mjs","names":["defaultResolveLink","resolveLink"],"sources":["../src/image.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 { ComponentType, ReactElement } from \"react\";\n\nimport { resolveLink as defaultResolveLink, type ResolveLink } from \"./fields/link\";\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`, `linkComponent` and `resolveLink`\n * are the four things a package block cannot decide for itself — the site's\n * page measure, how an image gets optimised, which router a link goes\n * through, and what path a CMS page lives at. All four arrive already\n * 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 /** How this block turns a link's destination into an `href`; `null` renders the label as text. */\n resolveLink: ResolveLink;\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\n/**\n * The attribute a block editor in the Payload admin (payload-better-editor)\n * walks up to from a click in its preview iframe, to find which row was\n * clicked. `RenderBlocks` puts it on a wrapper around every block, so a site\n * adopting the editor has nothing to add per block; a site's tests can assert\n * against the name here rather than retype it.\n */\nexport const BLOCK_ID_ATTRIBUTE = \"data-better-editor-id\";\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 * Defaults to `resolveLink`, which makes a page link `/<slug>`. A site\n * whose routes differ passes its own; like `linkComponent`, it must be\n * exported from a client module, since a Server Component cannot hand a\n * closure across the boundary.\n */\n resolveLink?: ResolveLink;\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 *\n * Each block sits inside an unstyled block-level `<div>` carrying\n * `BLOCK_ID_ATTRIBUTE`, which is what a click-to-edit overlay resolves and\n * what its hover outline paints on. A row with no `id` (a fixture, a row the\n * form has not saved) gets no attribute: there is no form-state path for an\n * editor to open. The band's own `<section>` and its full-bleed background\n * are unchanged by the wrapper.\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 resolveLink = defaultResolveLink,\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 <div\n key={block.id ?? index}\n {...(block.id ? { [BLOCK_ID_ATTRIBUTE]: block.id } : {})}\n >\n <Renderer\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 resolveLink={resolveLink}\n />\n </div>\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 resolveLink: resolve,\n}: BlockRendererProps<HeroBlockData>) {\n const image = resolveMedia(block.image);\n // A call to action with no destination (its page missing or unpublished,\n // or nothing picked yet on a draft) keeps its label as text.\n const links = (block.links ?? []).flatMap((link) =>\n link.label\n ? [{ label: link.label, href: resolve(link), newTab: link.newTab ?? false }]\n : [],\n );\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 ?? link.label}-${i}`}\n asChild\n variant={i === 0 ? \"default\" : \"outline\"}\n >\n {link.href ? (\n <Link\n href={link.href}\n newTab={link.newTab}\n {...newTabAttributes(link.newTab)}\n >\n {link.label}\n </Link>\n ) : (\n <span>{link.label}</span>\n )}\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 resolveLink: resolve,\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 // The link text is what makes a call to action; with no destination it\n // renders as text (see `renderLinkWith`).\n const hasCta = Boolean(cta?.linkText);\n const ctaHref = cta ? resolve(cta) : null;\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: ctaHref ?? \"\",\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 resolveLink: resolve,\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 // The label is what makes the link; with no destination it renders as\n // text (see `renderLinkWith`).\n const hasLink = Boolean(link?.label);\n const linkHref = link ? resolve(link) : null;\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: linkHref ?? \"\",\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 { resolveLink, type LinkValue, type ResolveLink } from \"../../fields/link\";\nimport { renderLinkWith } from \"../../link\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport type { MegaMenuBlockData, MegaMenuLinkData } from \"../../types\";\nimport { remWidth } from \"./rem-width\";\n\n/**\n * A menu link needs both halves. Unlike a call to action, a link whose page\n * is missing or unpublished is dropped rather than shown as text: a menu\n * item that goes nowhere is worse than one fewer item.\n */\nfunction completeLink(\n row: MegaMenuLinkData,\n resolve: ResolveLink,\n): MegaMenuLink | null {\n const href = resolve(row);\n if (!row.label || !href) return null;\n return {\n label: row.label,\n 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 * `resolve` turns each link's page into a path; the package's own is the\n * default.\n */\nexport function megaMenuPanelFromRow(\n row: MegaMenuBlockData,\n resolve: ResolveLink = resolveLink,\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((link) => completeLink(link, resolve))\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 = footerLink(panel.footer?.overview, resolve);\n const cta = footerLink(panel.footer?.cta, resolve);\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 ? { overview } : {}),\n ...(cta ? { cta } : {}),\n },\n };\n}\n\n/** A footer link, complete, or nothing: same rule as `completeLink`. */\nfunction footerLink(\n row: LinkValue | undefined,\n resolve: ResolveLink,\n): { label: string; href: string; newTab?: true } | null {\n const href = row ? resolve(row) : null;\n if (!row?.label || !href) return null;\n return {\n label: row.label,\n href,\n ...(row.newTab ? { newTab: true } : {}),\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 resolveLink: resolve,\n variants,\n icons,\n}: MegaMenuBlockRendererProps) {\n const panel = megaMenuPanelFromRow(block, resolve);\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 { resolveLink, type ResolveLink } from \"../../fields/link\";\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 /** The site's resolver for page links; defaults to the package's `resolveLink`. */\n resolveLink?: ResolveLink;\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. A bar item whose page is\n * missing or unpublished is dropped, like a panel link. A mega menu's own\n * `href` (the trigger's landing page) is plain text, not a picked page.\n */\nexport function headerItemsFromBlocks(\n blocks: HeaderBlockRow[],\n {\n variants,\n icons,\n isActive,\n linkComponent,\n resolveLink: resolve = resolveLink,\n }: HeaderItemsOptions = {},\n): FloatingNavItem[] {\n const items: MegaMenuItem[] = [];\n for (const row of blocks) {\n if (!row.label) continue;\n if (row.blockType === \"navLink\") {\n const href = resolve(row);\n if (!href) continue;\n items.push({ label: row.label, href });\n } else if (row.blockType === \"megaMenu\") {\n const panel = megaMenuPanelFromRow(row, resolve);\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":["defaultResolveLink","resolveLink"],"sources":["../src/image.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"],"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 { ComponentType, ReactElement } from \"react\";\n\nimport { resolveLink as defaultResolveLink, type ResolveLink } from \"./fields/link\";\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`, `linkComponent` and `resolveLink`\n * are the four things a package block cannot decide for itself — the site's\n * page measure, how an image gets optimised, which router a link goes\n * through, and what path a CMS page lives at. All four arrive already\n * 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 /** How this block turns a link's destination into an `href`; `null` renders the label as text. */\n resolveLink: ResolveLink;\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\n/**\n * The attribute a block editor in the Payload admin (payload-better-editor)\n * walks up to from a click in its preview iframe, to find which row was\n * clicked. `RenderBlocks` puts it on a wrapper around every block, so a site\n * adopting the editor has nothing to add per block; a site's tests can assert\n * against the name here rather than retype it.\n */\nexport const BLOCK_ID_ATTRIBUTE = \"data-better-editor-id\";\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 * Defaults to `resolveLink`, which makes a page link `/<slug>`. A site\n * whose routes differ passes its own; like `linkComponent`, it must be\n * exported from a client module, since a Server Component cannot hand a\n * closure across the boundary.\n */\n resolveLink?: ResolveLink;\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 *\n * Each block sits inside an unstyled block-level `<div>` carrying\n * `BLOCK_ID_ATTRIBUTE`, which is what a click-to-edit overlay resolves and\n * what its hover outline paints on. A row with no `id` (a fixture, a row the\n * form has not saved) gets no attribute: there is no form-state path for an\n * editor to open. The band's own `<section>` and its full-bleed background\n * are unchanged by the wrapper.\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 resolveLink = defaultResolveLink,\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 <div\n key={block.id ?? index}\n {...(block.id ? { [BLOCK_ID_ATTRIBUTE]: block.id } : {})}\n >\n <Renderer\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 resolveLink={resolveLink}\n />\n </div>\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 resolveLink: resolve,\n}: BlockRendererProps<HeroBlockData>) {\n const image = resolveMedia(block.image);\n // A call to action with no destination (its page missing or unpublished,\n // or nothing picked yet on a draft) keeps its label as text.\n const links = (block.links ?? []).flatMap((link) =>\n link.label\n ? [{ label: link.label, href: resolve(link), newTab: link.newTab ?? false }]\n : [],\n );\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 ?? link.label}-${i}`}\n asChild\n variant={i === 0 ? \"default\" : \"outline\"}\n >\n {link.href ? (\n <Link\n href={link.href}\n newTab={link.newTab}\n {...newTabAttributes(link.newTab)}\n >\n {link.label}\n </Link>\n ) : (\n <span>{link.label}</span>\n )}\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 resolveLink: resolve,\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 // The link text is what makes a call to action; with no destination it\n // renders as text (see `renderLinkWith`).\n const hasCta = Boolean(cta?.linkText);\n const ctaHref = cta ? resolve(cta) : null;\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: ctaHref ?? \"\",\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 resolveLink: resolve,\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 // The label is what makes the link; with no destination it renders as\n // text (see `renderLinkWith`).\n const hasLink = Boolean(link?.label);\n const linkHref = link ? resolve(link) : null;\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: linkHref ?? \"\",\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"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
 
2
- import { m as BlockRendererProps, s as RichTextBlockData, x as ResolveLink } from "./types-ODMRyOpH.mjs";
2
+ import { S as ResolveLink, c as RichTextBlockData, h as BlockRendererProps } from "./types-BurBx5me.mjs";
3
3
  import * as react_jsx_runtime0 from "react/jsx-runtime";
4
4
  import { SerializedLinkNode } from "@payloadcms/richtext-lexical";
5
5
 
@@ -1,5 +1,6 @@
1
1
  "use client";
2
- import { l as newTabAttributes, o as seamClasses, s as cx } from "./seam-CmLb3BSo.mjs";
2
+ import { n as newTabAttributes } from "./link-32261jXB.mjs";
3
+ import { o as seamClasses, s as cx } from "./seam-Cz7oiQ5z.mjs";
3
4
  import { jsx } from "react/jsx-runtime";
4
5
  import { RichText } from "@payloadcms/richtext-lexical/react";
5
6
  //#region src/blocks/rich-text/component.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"rich-text.mjs","names":[],"sources":["../src/blocks/rich-text/component.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { RichText, type JSXConvertersFunction } from \"@payloadcms/richtext-lexical/react\";\nimport type { SerializedEditorState } from \"@payloadcms/richtext-lexical/lexical\";\nimport type {\n SerializedAutoLinkNode,\n SerializedLinkNode,\n} from \"@payloadcms/richtext-lexical\";\n\nimport { cx } from \"../../cx\";\nimport type { LinkDestination, ResolveLink } from \"../../fields/link\";\nimport { type BlockLinkComponent, newTabAttributes } from \"../../link\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport { seamClasses } from \"../../seam\";\nimport type { RichTextBlockData } from \"../../types\";\n\n/**\n * How an editor's link to another document becomes a URL. The same argument\n * Payload's own `LinkJSXConverter` takes: `linkNode.fields.doc` carries the\n * `relationTo` and the related document (populated, or just its id, by the\n * depth the page was fetched at). Only the site knows its routes.\n */\nexport type InternalDocToHref = (args: { linkNode: SerializedLinkNode }) => string;\n\nexport interface RichTextBlockRendererOptions {\n /**\n * Wins when both this and `resolveLink` are passed, so a site that already\n * wrote a custom Lexical resolver is unchanged.\n */\n internalDocToHref?: InternalDocToHref;\n /**\n * The same function `RenderBlocks` takes. When `internalDocToHref` is\n * omitted, the factory builds one with `internalDocToHrefFrom`.\n */\n resolveLink?: ResolveLink;\n}\n\n/**\n * Turns the site's `resolveLink` into the Lexical resolver the rich-text\n * factory needs. Unpacks `linkNode.fields.doc` into a page destination and\n * calls `resolveLink`; `null` (unpopulated or unpublished) becomes `#`, the\n * same fallback BIS-69 uses when no resolver is registered.\n */\nexport function internalDocToHrefFrom(resolve: ResolveLink): InternalDocToHref {\n return ({ linkNode }) => {\n const doc = linkNode.fields.doc;\n const value = doc && typeof doc === \"object\" ? doc.value : undefined;\n const page = (value ?? null) as LinkDestination[\"page\"];\n return resolve({ type: \"page\", page }) ?? \"#\";\n };\n}\n\n/**\n * The `link` and `autolink` converters, through the seam. Payload's default\n * converters render both as a bare `<a>`, a full document load on a Next\n * site; these take their place and leave every other node to\n * `defaultJSXConverters`. `newTab` is expanded to `target`/`rel` the way\n * every other renderer does it, so an adapter that only spreads the anchor\n * attributes is still safe.\n *\n * One default converter still writes markup the seams exist to own: `upload`\n * renders a file an editor drops into the prose as a bare `<a>` and an image\n * as a raw `<img>`, outside `linkComponent` and `imageComponent` both. That\n * is BIS-76, pinned in renderers.test.tsx; until it ships, a rich-text\n * section is client-side for its links and not for its uploads.\n *\n * An internal link with no resolver cannot be resolved here, so it\n * renders through the seam at `#`, the href Payload's own converter falls back\n * to, and says so on the console rather than shipping a dead link quietly.\n */\nfunction linkConverters(\n Link: BlockLinkComponent,\n internalDocToHref: InternalDocToHref | undefined,\n): JSXConvertersFunction {\n const through = (\n href: string,\n node: SerializedAutoLinkNode | SerializedLinkNode,\n children: ReactNode,\n ) => (\n <Link href={href} newTab={node.fields.newTab} {...newTabAttributes(node.fields.newTab)}>\n {children}\n </Link>\n );\n return ({ defaultConverters }) => ({\n ...defaultConverters,\n autolink: ({ node, nodesToJSX }) =>\n through(node.fields.url ?? \"\", node, nodesToJSX({ nodes: node.children })),\n link: ({ node, nodesToJSX }) => {\n let href = node.fields.url ?? \"\";\n if (node.fields.linkType === \"internal\") {\n if (internalDocToHref) {\n href = internalDocToHref({ linkNode: node });\n } else {\n console.error(\n \"@bison-lab/payload-blocks: a rich-text link points at another document, but the richText renderer has no internalDocToHref to turn it into a URL. Register richTextBlockRenderer({ resolveLink }) or richTextBlockRenderer({ internalDocToHref }) in a client module instead of RichTextBlockRenderer.\",\n );\n href = \"#\";\n }\n }\n return through(href, node, nodesToJSX({ nodes: node.children }));\n },\n });\n}\n\n/**\n * Builds the `richText` renderer for a site.\n *\n * `RichTextBlockRenderer` below is the plain registry value and is what a\n * site with no internal links needs. A site whose editors link to other\n * documents builds its own with `resolveLink` (the same function\n * `RenderBlocks` takes; the factory wraps it with `internalDocToHrefFrom`)\n * or a hand-written `internalDocToHref`. When both are passed,\n * `internalDocToHref` wins. It must do so in a `\"use client\"` module: this\n * entry carries the client banner, so the factory is a client reference\n * that a Server Component can pass along but cannot call, and the resolver\n * is a closure that cannot cross the boundary as a prop either. The site's\n * `next/link` adapter lives in the same kind of module for the same reason.\n */\nexport function richTextBlockRenderer({\n internalDocToHref,\n resolveLink: resolve,\n}: RichTextBlockRendererOptions = {}) {\n const toHref = internalDocToHref ?? (resolve ? internalDocToHrefFrom(resolve) : undefined);\n /**\n * A Lexical document as prose.\n *\n * This is the one renderer that needs `@payloadcms/richtext-lexical`, which\n * is 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 */\n function RichTextBlockRenderer({\n block,\n overlapAbove,\n overlapBelow,\n containerClassName,\n linkComponent,\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 converters={linkConverters(linkComponent, toHref)}\n className=\"max-w-prose\"\n />\n </div>\n </section>\n );\n }\n return RichTextBlockRenderer;\n}\n\n/** The `richText` renderer with no internal-link resolver; see the factory. */\nexport const RichTextBlockRenderer = richTextBlockRenderer();\n"],"mappings":";;;;;;;;;;;AA0CA,SAAgB,sBAAsB,SAAyC;AAC7E,SAAQ,EAAE,eAAe;EACvB,MAAM,MAAM,SAAS,OAAO;AAG5B,SAAO,QAAQ;GAAE,MAAM;GAAQ,OAFjB,OAAO,OAAO,QAAQ,WAAW,IAAI,QAAQ,KAAA,MACpC;GACc,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;AAsB9C,SAAS,eACP,MACA,mBACuB;CACvB,MAAM,WACJ,MACA,MACA,aAEA,oBAAC,MAAD;EAAY;EAAM,QAAQ,KAAK,OAAO;EAAQ,GAAI,iBAAiB,KAAK,OAAO,OAAO;EACnF;EACI,CAAA;AAET,SAAQ,EAAE,yBAAyB;EACjC,GAAG;EACH,WAAW,EAAE,MAAM,iBACjB,QAAQ,KAAK,OAAO,OAAO,IAAI,MAAM,WAAW,EAAE,OAAO,KAAK,UAAU,CAAC,CAAC;EAC5E,OAAO,EAAE,MAAM,iBAAiB;GAC9B,IAAI,OAAO,KAAK,OAAO,OAAO;AAC9B,OAAI,KAAK,OAAO,aAAa,WAC3B,KAAI,kBACF,QAAO,kBAAkB,EAAE,UAAU,MAAM,CAAC;QACvC;AACL,YAAQ,MACN,ySACD;AACD,WAAO;;AAGX,UAAO,QAAQ,MAAM,MAAM,WAAW,EAAE,OAAO,KAAK,UAAU,CAAC,CAAC;;EAEnE;;;;;;;;;;;;;;;;AAiBH,SAAgB,sBAAsB,EACpC,mBACA,aAAa,YACmB,EAAE,EAAE;CACpC,MAAM,SAAS,sBAAsB,UAAU,sBAAsB,QAAQ,GAAG,KAAA;;;;;;;;;;;;;CAahF,SAAS,sBAAsB,EAC7B,OACA,cACA,cACA,oBACA,iBACwC;AACxC,MAAI,CAAC,MAAM,QAAS,QAAO;AAC3B,SACE,oBAAC,WAAD;GAAS,WAAW,YAAY;IAAE;IAAc;IAAc,CAAC;aAC7D,oBAAC,OAAD;IAAK,WAAW,GAAG,oBAAoB,iBAAiB;cACtD,oBAAC,UAAD;KACE,MAAM,MAAM;KACZ,YAAY,eAAe,eAAe,OAAO;KACjD,WAAU;KACV,CAAA;IACE,CAAA;GACE,CAAA;;AAGd,QAAO;;;AAIT,MAAa,wBAAwB,uBAAuB"}
1
+ {"version":3,"file":"rich-text.mjs","names":[],"sources":["../src/blocks/rich-text/component.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { RichText, type JSXConvertersFunction } from \"@payloadcms/richtext-lexical/react\";\nimport type { SerializedEditorState } from \"@payloadcms/richtext-lexical/lexical\";\nimport type {\n SerializedAutoLinkNode,\n SerializedLinkNode,\n} from \"@payloadcms/richtext-lexical\";\n\nimport { cx } from \"../../cx\";\nimport type { LinkDestination, ResolveLink } from \"../../fields/link\";\nimport { type BlockLinkComponent, newTabAttributes } from \"../../link\";\nimport type { BlockRendererProps } from \"../../render-blocks\";\nimport { seamClasses } from \"../../seam\";\nimport type { RichTextBlockData } from \"../../types\";\n\n/**\n * How an editor's link to another document becomes a URL. The same argument\n * Payload's own `LinkJSXConverter` takes: `linkNode.fields.doc` carries the\n * `relationTo` and the related document (populated, or just its id, by the\n * depth the page was fetched at). Only the site knows its routes.\n */\nexport type InternalDocToHref = (args: { linkNode: SerializedLinkNode }) => string;\n\nexport interface RichTextBlockRendererOptions {\n /**\n * Wins when both this and `resolveLink` are passed, so a site that already\n * wrote a custom Lexical resolver is unchanged.\n */\n internalDocToHref?: InternalDocToHref;\n /**\n * The same function `RenderBlocks` takes. When `internalDocToHref` is\n * omitted, the factory builds one with `internalDocToHrefFrom`.\n */\n resolveLink?: ResolveLink;\n}\n\n/**\n * Turns the site's `resolveLink` into the Lexical resolver the rich-text\n * factory needs. Unpacks `linkNode.fields.doc` into a page destination and\n * calls `resolveLink`; `null` (unpopulated or unpublished) becomes `#`, the\n * same fallback BIS-69 uses when no resolver is registered.\n */\nexport function internalDocToHrefFrom(resolve: ResolveLink): InternalDocToHref {\n return ({ linkNode }) => {\n const doc = linkNode.fields.doc;\n const value = doc && typeof doc === \"object\" ? doc.value : undefined;\n const page = (value ?? null) as LinkDestination[\"page\"];\n return resolve({ type: \"page\", page }) ?? \"#\";\n };\n}\n\n/**\n * The `link` and `autolink` converters, through the seam. Payload's default\n * converters render both as a bare `<a>`, a full document load on a Next\n * site; these take their place and leave every other node to\n * `defaultJSXConverters`. `newTab` is expanded to `target`/`rel` the way\n * every other renderer does it, so an adapter that only spreads the anchor\n * attributes is still safe.\n *\n * One default converter still writes markup the seams exist to own: `upload`\n * renders a file an editor drops into the prose as a bare `<a>` and an image\n * as a raw `<img>`, outside `linkComponent` and `imageComponent` both. That\n * is BIS-76, pinned in renderers.test.tsx; until it ships, a rich-text\n * section is client-side for its links and not for its uploads.\n *\n * An internal link with no resolver cannot be resolved here, so it\n * renders through the seam at `#`, the href Payload's own converter falls back\n * to, and says so on the console rather than shipping a dead link quietly.\n */\nfunction linkConverters(\n Link: BlockLinkComponent,\n internalDocToHref: InternalDocToHref | undefined,\n): JSXConvertersFunction {\n const through = (\n href: string,\n node: SerializedAutoLinkNode | SerializedLinkNode,\n children: ReactNode,\n ) => (\n <Link href={href} newTab={node.fields.newTab} {...newTabAttributes(node.fields.newTab)}>\n {children}\n </Link>\n );\n return ({ defaultConverters }) => ({\n ...defaultConverters,\n autolink: ({ node, nodesToJSX }) =>\n through(node.fields.url ?? \"\", node, nodesToJSX({ nodes: node.children })),\n link: ({ node, nodesToJSX }) => {\n let href = node.fields.url ?? \"\";\n if (node.fields.linkType === \"internal\") {\n if (internalDocToHref) {\n href = internalDocToHref({ linkNode: node });\n } else {\n console.error(\n \"@bison-lab/payload-blocks: a rich-text link points at another document, but the richText renderer has no internalDocToHref to turn it into a URL. Register richTextBlockRenderer({ resolveLink }) or richTextBlockRenderer({ internalDocToHref }) in a client module instead of RichTextBlockRenderer.\",\n );\n href = \"#\";\n }\n }\n return through(href, node, nodesToJSX({ nodes: node.children }));\n },\n });\n}\n\n/**\n * Builds the `richText` renderer for a site.\n *\n * `RichTextBlockRenderer` below is the plain registry value and is what a\n * site with no internal links needs. A site whose editors link to other\n * documents builds its own with `resolveLink` (the same function\n * `RenderBlocks` takes; the factory wraps it with `internalDocToHrefFrom`)\n * or a hand-written `internalDocToHref`. When both are passed,\n * `internalDocToHref` wins. It must do so in a `\"use client\"` module: this\n * entry carries the client banner, so the factory is a client reference\n * that a Server Component can pass along but cannot call, and the resolver\n * is a closure that cannot cross the boundary as a prop either. The site's\n * `next/link` adapter lives in the same kind of module for the same reason.\n */\nexport function richTextBlockRenderer({\n internalDocToHref,\n resolveLink: resolve,\n}: RichTextBlockRendererOptions = {}) {\n const toHref = internalDocToHref ?? (resolve ? internalDocToHrefFrom(resolve) : undefined);\n /**\n * A Lexical document as prose.\n *\n * This is the one renderer that needs `@payloadcms/richtext-lexical`, which\n * is 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 */\n function RichTextBlockRenderer({\n block,\n overlapAbove,\n overlapBelow,\n containerClassName,\n linkComponent,\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 converters={linkConverters(linkComponent, toHref)}\n className=\"max-w-prose\"\n />\n </div>\n </section>\n );\n }\n return RichTextBlockRenderer;\n}\n\n/** The `richText` renderer with no internal-link resolver; see the factory. */\nexport const RichTextBlockRenderer = richTextBlockRenderer();\n"],"mappings":";;;;;;;;;;;;AA0CA,SAAgB,sBAAsB,SAAyC;AAC7E,SAAQ,EAAE,eAAe;EACvB,MAAM,MAAM,SAAS,OAAO;AAG5B,SAAO,QAAQ;GAAE,MAAM;GAAQ,OAFjB,OAAO,OAAO,QAAQ,WAAW,IAAI,QAAQ,KAAA,MACpC;GACc,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;AAsB9C,SAAS,eACP,MACA,mBACuB;CACvB,MAAM,WACJ,MACA,MACA,aAEA,oBAAC,MAAD;EAAY;EAAM,QAAQ,KAAK,OAAO;EAAQ,GAAI,iBAAiB,KAAK,OAAO,OAAO;EACnF;EACI,CAAA;AAET,SAAQ,EAAE,yBAAyB;EACjC,GAAG;EACH,WAAW,EAAE,MAAM,iBACjB,QAAQ,KAAK,OAAO,OAAO,IAAI,MAAM,WAAW,EAAE,OAAO,KAAK,UAAU,CAAC,CAAC;EAC5E,OAAO,EAAE,MAAM,iBAAiB;GAC9B,IAAI,OAAO,KAAK,OAAO,OAAO;AAC9B,OAAI,KAAK,OAAO,aAAa,WAC3B,KAAI,kBACF,QAAO,kBAAkB,EAAE,UAAU,MAAM,CAAC;QACvC;AACL,YAAQ,MACN,ySACD;AACD,WAAO;;AAGX,UAAO,QAAQ,MAAM,MAAM,WAAW,EAAE,OAAO,KAAK,UAAU,CAAC,CAAC;;EAEnE;;;;;;;;;;;;;;;;AAiBH,SAAgB,sBAAsB,EACpC,mBACA,aAAa,YACmB,EAAE,EAAE;CACpC,MAAM,SAAS,sBAAsB,UAAU,sBAAsB,QAAQ,GAAG,KAAA;;;;;;;;;;;;;CAahF,SAAS,sBAAsB,EAC7B,OACA,cACA,cACA,oBACA,iBACwC;AACxC,MAAI,CAAC,MAAM,QAAS,QAAO;AAC3B,SACE,oBAAC,WAAD;GAAS,WAAW,YAAY;IAAE;IAAc;IAAc,CAAC;aAC7D,oBAAC,OAAD;IAAK,WAAW,GAAG,oBAAoB,iBAAiB;cACtD,oBAAC,UAAD;KACE,MAAM,MAAM;KACZ,YAAY,eAAe,eAAe,OAAO;KACjD,WAAU;KACV,CAAA;IACE,CAAA;GACE,CAAA;;AAGd,QAAO;;;AAIT,MAAa,wBAAwB,uBAAuB"}
@@ -1,49 +1,4 @@
1
1
  "use client";
2
- import { jsx } from "react/jsx-runtime";
3
- //#region src/link.tsx
4
- /** `target` and `rel` together, or neither: a bare `target` is a tabnabbing hole. */
5
- function newTabAttributes(newTab) {
6
- return newTab ? {
7
- target: "_blank",
8
- rel: "noopener noreferrer"
9
- } : {};
10
- }
11
- /**
12
- * The fallback when no `linkComponent` is supplied: a plain `<a>`. Correct
13
- * everywhere and client-side nowhere, which is the right default for a package
14
- * that cannot know what router it landed in.
15
- */
16
- function DefaultBlockLink({ href, newTab, children, ...rest }) {
17
- return /* @__PURE__ */ jsx("a", {
18
- href,
19
- ...rest,
20
- ...newTabAttributes(newTab),
21
- children
22
- });
23
- }
24
- DefaultBlockLink.displayName = "DefaultBlockLink";
25
- /**
26
- * Adapts the seam to the `renderLink` prop every `@bison-lab/ui` block takes.
27
- * The ui blocks hand over `target`/`rel` already expanded, so `newTab` is
28
- * read back off `target` to keep the flag and the attributes in step.
29
- *
30
- * An empty `href` is a link with no destination: a page link whose page is
31
- * missing or unpublished (`resolveLink` gave `null`). It renders as text in
32
- * the same place, with the same class, so a call to action never points
33
- * nowhere and never throws.
34
- */
35
- function renderLinkWith(Link) {
36
- return ({ href, children, ...rest }) => href ? /* @__PURE__ */ jsx(Link, {
37
- href,
38
- newTab: rest.target === "_blank",
39
- ...rest,
40
- children
41
- }) : /* @__PURE__ */ jsx("span", {
42
- className: rest.className,
43
- children
44
- });
45
- }
46
- //#endregion
47
2
  //#region src/cx.ts
48
3
  /**
49
4
  * Class joining, locally.
@@ -94,6 +49,6 @@ function seamClasses({ overlapAbove, overlapBelow }) {
94
49
  return cx(overlapAbove && "pt-16 lg:pt-20", overlapBelow && "pb-16 lg:pb-20");
95
50
  }
96
51
  //#endregion
97
- export { overlapsSeam as a, DefaultBlockLink as c, SEAM_PULL_UP as i, newTabAttributes as l, OVERLAP_BELOW_CLEARANCE as n, seamClasses as o, SEAM_PULL_DOWN as r, cx as s, OVERLAP_ABOVE_CLEARANCE as t, renderLinkWith as u };
52
+ export { overlapsSeam as a, SEAM_PULL_UP as i, OVERLAP_BELOW_CLEARANCE as n, seamClasses as o, SEAM_PULL_DOWN as r, cx as s, OVERLAP_ABOVE_CLEARANCE as t };
98
53
 
99
- //# sourceMappingURL=seam-CmLb3BSo.mjs.map
54
+ //# sourceMappingURL=seam-Cz7oiQ5z.mjs.map