@bison-lab/payload-blocks 3.2.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  "use client";
2
- import { t as cx } from "./cx-BKHSv3xT.mjs";
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";
3
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
- import { Button, FAQColumnsBlock, JsonLd, MegaMenuPanel, NapBlock, ProcessStepsBlock, ShowcasePanelsBlock, TestimonialMasonry, megaMenuToFloatingNavItems } from "@bison-lab/ui";
5
+ import { Button, FAQColumnsBlock, JsonLd, MegaMenuPanel, NapBlock, ProcessStepsBlock, ShowcasePanelsBlock, StatsBandBlock, TestimonialMasonry, megaMenuToFloatingNavItems } from "@bison-lab/ui";
5
6
  //#region src/image.tsx
6
7
  /**
7
8
  * The fallback when no `imageComponent` is supplied: a plain `<img>`. Correct
@@ -21,46 +22,18 @@ function DefaultBlockImage({ src, alt, width, height, className, sizes, priority
21
22
  });
22
23
  }
23
24
  //#endregion
24
- //#region src/link.tsx
25
- /** `target` and `rel` together, or neither: a bare `target` is a tabnabbing hole. */
26
- function newTabAttributes(newTab) {
27
- return newTab ? {
28
- target: "_blank",
29
- rel: "noopener noreferrer"
30
- } : {};
31
- }
32
- /**
33
- * The fallback when no `linkComponent` is supplied: a plain `<a>`. Correct
34
- * everywhere and client-side nowhere, which is the right default for a package
35
- * that cannot know what router it landed in.
36
- */
37
- function DefaultBlockLink({ href, newTab, children, ...rest }) {
38
- return /* @__PURE__ */ jsx("a", {
39
- href,
40
- ...rest,
41
- ...newTabAttributes(newTab),
42
- children
43
- });
44
- }
45
- DefaultBlockLink.displayName = "DefaultBlockLink";
46
- /**
47
- * Adapts the seam to the `renderLink` prop every `@bison-lab/ui` block takes.
48
- * The ui blocks hand over `target`/`rel` already expanded, so `newTab` is
49
- * read back off `target` to keep the flag and the attributes in step.
50
- */
51
- function renderLinkWith(Link) {
52
- return ({ href, children, ...rest }) => /* @__PURE__ */ jsx(Link, {
53
- href,
54
- newTab: rest.target === "_blank",
55
- ...rest,
56
- children
57
- });
58
- }
59
- //#endregion
60
25
  //#region src/render-blocks.tsx
61
26
  /** Fallback page measure for a site that does not pass its own. */
62
27
  const DEFAULT_CONTAINER = "mx-auto w-full max-w-7xl px-6";
63
28
  /**
29
+ * The attribute a block editor in the Payload admin (payload-better-editor)
30
+ * walks up to from a click in its preview iframe, to find which row was
31
+ * clicked. `RenderBlocks` puts it on a wrapper around every block, so a site
32
+ * adopting the editor has nothing to add per block; a site's tests can assert
33
+ * against the name here rather than retype it.
34
+ */
35
+ const BLOCK_ID_ATTRIBUTE = "data-better-editor-id";
36
+ /**
64
37
  * A row saved against a block that has since left the config has no renderer.
65
38
  * It is dropped before anything is counted, so its neighbours see each other,
66
39
  * not a gap: the block after it gets the real `prevType`, a run of one type is
@@ -85,19 +58,33 @@ function runIndexes(blocks) {
85
58
  /**
86
59
  * Renders a page's blocks in order through the registry. A Server Component:
87
60
  * it only maps and looks up, and each renderer decides its own nature.
61
+ *
62
+ * Each block sits inside an unstyled block-level `<div>` carrying
63
+ * `BLOCK_ID_ATTRIBUTE`, which is what a click-to-edit overlay resolves and
64
+ * what its hover outline paints on. A row with no `id` (a fixture, a row the
65
+ * form has not saved) gets no attribute: there is no form-state path for an
66
+ * editor to open. The band's own `<section>` and its full-bleed background
67
+ * are unchanged by the wrapper.
88
68
  */
89
- function RenderBlocks({ blocks, registry, containerClassName = DEFAULT_CONTAINER, imageComponent = DefaultBlockImage, linkComponent = DefaultBlockLink }) {
69
+ function RenderBlocks({ blocks, registry, containerClassName = DEFAULT_CONTAINER, imageComponent = DefaultBlockImage, linkComponent = DefaultBlockLink, floats = overlapsSeam, resolveLink: resolveLink$1 = resolveLink }) {
90
70
  const rows = renderable(blocks, registry);
91
71
  const runs = runIndexes(rows.map((row) => row.block));
92
- return /* @__PURE__ */ jsx(Fragment, { children: rows.map(({ block, Renderer }, index) => /* @__PURE__ */ jsx(Renderer, {
93
- block,
94
- index,
95
- prevType: index > 0 ? rows[index - 1].block.blockType : void 0,
96
- runIndex: runs[index],
97
- isLast: index === rows.length - 1,
98
- containerClassName,
99
- imageComponent,
100
- linkComponent
72
+ const floating = rows.map((row) => floats(row.block));
73
+ return /* @__PURE__ */ jsx(Fragment, { children: rows.map(({ block, Renderer }, index) => /* @__PURE__ */ jsx("div", {
74
+ ...block.id ? { [BLOCK_ID_ATTRIBUTE]: block.id } : {},
75
+ children: /* @__PURE__ */ jsx(Renderer, {
76
+ block,
77
+ index,
78
+ prevType: index > 0 ? rows[index - 1].block.blockType : void 0,
79
+ runIndex: runs[index],
80
+ isLast: index === rows.length - 1,
81
+ overlapAbove: index > 0 && floating[index - 1],
82
+ overlapBelow: index < rows.length - 1 && floating[index + 1],
83
+ containerClassName,
84
+ imageComponent,
85
+ linkComponent,
86
+ resolveLink: resolveLink$1
87
+ })
101
88
  }, block.id ?? index)) });
102
89
  }
103
90
  //#endregion
@@ -146,12 +133,19 @@ const TONES = {
146
133
  * render at all, not to be the final answer. A site overrides this one registry
147
134
  * entry with its own and keeps every other block.
148
135
  */
149
- function HeroBlockRenderer({ block, index, containerClassName, imageComponent: Image, linkComponent: Link }) {
136
+ function HeroBlockRenderer({ block, index, overlapAbove, overlapBelow, containerClassName, imageComponent: Image, linkComponent: Link, resolveLink: resolve }) {
150
137
  const image = resolveMedia(block.image);
151
- const links = (block.links ?? []).filter((link) => link.href && link.label);
138
+ const links = (block.links ?? []).flatMap((link) => link.label ? [{
139
+ label: link.label,
140
+ href: resolve(link),
141
+ newTab: link.newTab ?? false
142
+ }] : []);
152
143
  const tone = TONES[block.tone ?? "sweep"];
153
144
  return /* @__PURE__ */ jsxs("section", {
154
- className: cx("relative isolate overflow-hidden", tone),
145
+ className: cx("relative isolate overflow-hidden", tone, seamClasses({
146
+ overlapAbove,
147
+ overlapBelow
148
+ })),
155
149
  children: [image ? /* @__PURE__ */ jsx("div", {
156
150
  className: "absolute inset-0 -z-10 opacity-25",
157
151
  children: /* @__PURE__ */ jsx(Image, {
@@ -185,13 +179,13 @@ function HeroBlockRenderer({ block, index, containerClassName, imageComponent: I
185
179
  children: links.map((link, i) => /* @__PURE__ */ jsx(Button, {
186
180
  asChild: true,
187
181
  variant: i === 0 ? "default" : "outline",
188
- children: /* @__PURE__ */ jsx(Link, {
182
+ children: link.href ? /* @__PURE__ */ jsx(Link, {
189
183
  href: link.href,
190
- newTab: link.newTab ?? false,
184
+ newTab: link.newTab,
191
185
  ...newTabAttributes(link.newTab),
192
186
  children: link.label
193
- })
194
- }, `${link.href}-${i}`))
187
+ }) : /* @__PURE__ */ jsx("span", { children: link.label })
188
+ }, `${link.href ?? link.label}-${i}`))
195
189
  }) : null
196
190
  ]
197
191
  })
@@ -200,7 +194,7 @@ function HeroBlockRenderer({ block, index, containerClassName, imageComponent: I
200
194
  }
201
195
  //#endregion
202
196
  //#region src/blocks/showcase-panels/component.tsx
203
- function ShowcasePanelsBlockRenderer({ block, containerClassName, imageComponent: Image, linkComponent }) {
197
+ function ShowcasePanelsBlockRenderer({ block, overlapAbove, overlapBelow, containerClassName, imageComponent: Image, linkComponent }) {
204
198
  const dims = [];
205
199
  const items = [];
206
200
  for (const [i, row] of (block.items ?? []).entries()) {
@@ -221,27 +215,33 @@ function ShowcasePanelsBlockRenderer({ block, containerClassName, imageComponent
221
215
  }
222
216
  if (items.length === 0) return null;
223
217
  return /* @__PURE__ */ jsx("section", {
224
- className: cx(containerClassName, "py-16 lg:py-24"),
225
- children: /* @__PURE__ */ jsx(ShowcasePanelsBlock, {
226
- items,
227
- spineVariant: block.spineVariant ?? "numbered",
228
- defaultActiveIndex: Math.min(Math.max(block.defaultActiveIndex ?? 0, 0), items.length - 1),
229
- ...block.watermark ? { watermark: block.watermark } : {},
230
- renderLink: renderLinkWith(linkComponent),
231
- renderImage: (item, i) => /* @__PURE__ */ jsx(Image, {
232
- src: item.image.src,
233
- alt: item.image.alt ?? "",
234
- width: dims[i]?.width,
235
- height: dims[i]?.height,
236
- className: "absolute inset-0 h-full w-full object-cover",
237
- sizes: "(min-width: 1024px) 50vw, 100vw"
218
+ className: seamClasses({
219
+ overlapAbove,
220
+ overlapBelow
221
+ }),
222
+ children: /* @__PURE__ */ jsx("div", {
223
+ className: cx(containerClassName, "py-16 lg:py-24"),
224
+ children: /* @__PURE__ */ jsx(ShowcasePanelsBlock, {
225
+ items,
226
+ spineVariant: block.spineVariant ?? "numbered",
227
+ defaultActiveIndex: Math.min(Math.max(block.defaultActiveIndex ?? 0, 0), items.length - 1),
228
+ ...block.watermark ? { watermark: block.watermark } : {},
229
+ renderLink: renderLinkWith(linkComponent),
230
+ renderImage: (item, i) => /* @__PURE__ */ jsx(Image, {
231
+ src: item.image.src,
232
+ alt: item.image.alt ?? "",
233
+ width: dims[i]?.width,
234
+ height: dims[i]?.height,
235
+ className: "absolute inset-0 h-full w-full object-cover",
236
+ sizes: "(min-width: 1024px) 50vw, 100vw"
237
+ })
238
238
  })
239
239
  })
240
240
  });
241
241
  }
242
242
  //#endregion
243
243
  //#region src/blocks/process-steps/component.tsx
244
- function ProcessStepsBlockRenderer({ block, containerClassName, imageComponent: Image }) {
244
+ function ProcessStepsBlockRenderer({ block, overlapAbove, overlapBelow, containerClassName, imageComponent: Image }) {
245
245
  const dims = [];
246
246
  const items = [];
247
247
  for (const [i, row] of (block.items ?? []).entries()) {
@@ -260,27 +260,33 @@ function ProcessStepsBlockRenderer({ block, containerClassName, imageComponent:
260
260
  }
261
261
  if (items.length === 0) return null;
262
262
  return /* @__PURE__ */ jsx("section", {
263
- className: cx(containerClassName, "py-16 lg:py-24"),
264
- children: /* @__PURE__ */ jsx(ProcessStepsBlock, {
265
- items,
266
- autoAdvance: block.autoAdvance ?? true,
267
- autoAdvanceDuration: block.autoAdvanceDuration ?? 6e3,
268
- pauseOnHover: block.pauseOnHover ?? true,
269
- defaultActiveIndex: Math.min(Math.max(block.defaultActiveIndex ?? 0, 0), items.length - 1),
270
- renderImage: (item, i) => /* @__PURE__ */ jsx(Image, {
271
- src: item.image.src,
272
- alt: item.image.alt ?? "",
273
- width: dims[i]?.width,
274
- height: dims[i]?.height,
275
- className: "size-full object-cover",
276
- sizes: "(min-width: 768px) 66vw, 100vw"
263
+ className: seamClasses({
264
+ overlapAbove,
265
+ overlapBelow
266
+ }),
267
+ children: /* @__PURE__ */ jsx("div", {
268
+ className: cx(containerClassName, "py-16 lg:py-24"),
269
+ children: /* @__PURE__ */ jsx(ProcessStepsBlock, {
270
+ items,
271
+ autoAdvance: block.autoAdvance ?? true,
272
+ autoAdvanceDuration: block.autoAdvanceDuration ?? 6e3,
273
+ pauseOnHover: block.pauseOnHover ?? true,
274
+ defaultActiveIndex: Math.min(Math.max(block.defaultActiveIndex ?? 0, 0), items.length - 1),
275
+ renderImage: (item, i) => /* @__PURE__ */ jsx(Image, {
276
+ src: item.image.src,
277
+ alt: item.image.alt ?? "",
278
+ width: dims[i]?.width,
279
+ height: dims[i]?.height,
280
+ className: "size-full object-cover",
281
+ sizes: "(min-width: 768px) 66vw, 100vw"
282
+ })
277
283
  })
278
284
  })
279
285
  });
280
286
  }
281
287
  //#endregion
282
288
  //#region src/blocks/faq-columns/component.tsx
283
- function FaqColumnsBlockRenderer({ block, containerClassName, linkComponent }) {
289
+ function FaqColumnsBlockRenderer({ block, overlapAbove, overlapBelow, containerClassName, linkComponent, resolveLink: resolve }) {
284
290
  const items = (block.items ?? []).filter((row) => row.question && row.answer).map((row, i) => ({
285
291
  id: row.id ?? String(i),
286
292
  question: row.question,
@@ -291,32 +297,39 @@ function FaqColumnsBlockRenderer({ block, containerClassName, linkComponent }) {
291
297
  }));
292
298
  if (items.length === 0) return null;
293
299
  const cta = block.cta;
294
- const hasCta = Boolean(cta?.href && cta?.linkText);
295
- return /* @__PURE__ */ jsx("section", { children: /* @__PURE__ */ jsx("div", {
296
- className: cx(containerClassName, "py-16 lg:py-24"),
297
- children: /* @__PURE__ */ jsx(FAQColumnsBlock, {
298
- className: "py-0 md:py-0",
299
- containerClassName: "max-w-none px-0 sm:px-0",
300
- items,
301
- title: block.title ?? "",
302
- ...block.eyebrow ? { eyebrow: block.eyebrow } : {},
303
- ...block.description ? { description: block.description } : {},
304
- sticky: block.sticky ?? true,
305
- type: block.type ?? "single",
306
- collapsible: block.collapsible ?? true,
307
- renderLink: renderLinkWith(linkComponent),
308
- ...hasCta && cta ? { cta: {
309
- title: cta.title ?? "",
310
- linkText: cta.linkText,
311
- href: cta.href,
312
- newTab: cta.newTab ?? false
313
- } } : {}
300
+ const hasCta = Boolean(cta?.linkText);
301
+ const ctaHref = cta ? resolve(cta) : null;
302
+ return /* @__PURE__ */ jsx("section", {
303
+ className: seamClasses({
304
+ overlapAbove,
305
+ overlapBelow
306
+ }),
307
+ children: /* @__PURE__ */ jsx("div", {
308
+ className: cx(containerClassName, "py-16 lg:py-24"),
309
+ children: /* @__PURE__ */ jsx(FAQColumnsBlock, {
310
+ className: "py-0 md:py-0",
311
+ containerClassName: "max-w-none px-0 sm:px-0",
312
+ items,
313
+ title: block.title ?? "",
314
+ ...block.eyebrow ? { eyebrow: block.eyebrow } : {},
315
+ ...block.description ? { description: block.description } : {},
316
+ sticky: block.sticky ?? true,
317
+ type: block.type ?? "single",
318
+ collapsible: block.collapsible ?? true,
319
+ renderLink: renderLinkWith(linkComponent),
320
+ ...hasCta && cta ? { cta: {
321
+ title: cta.title ?? "",
322
+ linkText: cta.linkText,
323
+ href: ctaHref ?? "",
324
+ newTab: cta.newTab ?? false
325
+ } } : {}
326
+ })
314
327
  })
315
- }) });
328
+ });
316
329
  }
317
330
  //#endregion
318
331
  //#region src/blocks/testimonial-masonry/component.tsx
319
- function TestimonialMasonryBlockRenderer({ block, containerClassName, linkComponent }) {
332
+ function TestimonialMasonryBlockRenderer({ block, overlapAbove, overlapBelow, containerClassName, linkComponent, resolveLink: resolve }) {
320
333
  const items = (block.items ?? []).filter((row) => row.content && row.author?.name).map((row, i) => {
321
334
  const avatar = resolveMedia(row.author?.avatar);
322
335
  return {
@@ -331,9 +344,13 @@ function TestimonialMasonryBlockRenderer({ block, containerClassName, linkCompon
331
344
  });
332
345
  if (items.length === 0) return null;
333
346
  const link = block.link;
334
- const hasLink = Boolean(link?.href && link?.label);
347
+ const hasLink = Boolean(link?.label);
348
+ const linkHref = link ? resolve(link) : null;
335
349
  return /* @__PURE__ */ jsx("section", {
336
- className: "bg-accent",
350
+ className: cx("bg-accent", seamClasses({
351
+ overlapAbove,
352
+ overlapBelow
353
+ })),
337
354
  children: /* @__PURE__ */ jsx("div", {
338
355
  className: cx(containerClassName, "py-16 lg:py-24"),
339
356
  children: /* @__PURE__ */ jsx(TestimonialMasonry, {
@@ -348,7 +365,7 @@ function TestimonialMasonryBlockRenderer({ block, containerClassName, linkCompon
348
365
  ...typeof block.maxVisibleRows === "number" ? { maxVisibleRows: block.maxVisibleRows } : {},
349
366
  ...hasLink && link ? { link: {
350
367
  label: link.label,
351
- href: link.href,
368
+ href: linkHref ?? "",
352
369
  newTab: link.newTab ?? false
353
370
  } } : {}
354
371
  })
@@ -357,7 +374,7 @@ function TestimonialMasonryBlockRenderer({ block, containerClassName, linkCompon
357
374
  }
358
375
  //#endregion
359
376
  //#region src/blocks/nap/component.tsx
360
- function NapBlockRenderer({ block, containerClassName, linkComponent }) {
377
+ function NapBlockRenderer({ block, overlapAbove, overlapBelow, containerClassName, linkComponent }) {
361
378
  const address = block.address;
362
379
  const departments = (block.departments ?? []).filter((row) => row.name && row.phoneE164).map((row, i) => ({
363
380
  id: row.id ?? String(i),
@@ -382,15 +399,61 @@ function NapBlockRenderer({ block, containerClassName, linkComponent }) {
382
399
  departments,
383
400
  ...block.url ? { url: block.url } : {}
384
401
  };
385
- return /* @__PURE__ */ jsx("section", { children: /* @__PURE__ */ jsxs("div", {
386
- className: cx(containerClassName, "py-16 lg:py-24"),
387
- children: [/* @__PURE__ */ jsx(NapBlock, {
388
- record,
389
- showName: block.showName ?? true,
390
- headingLevel: block.headingLevel ?? "h2",
391
- renderLink: renderLinkWith(linkComponent)
392
- }), block.emitJsonLd === false ? null : /* @__PURE__ */ jsx(JsonLd, { data: record })]
393
- }) });
402
+ return /* @__PURE__ */ jsx("section", {
403
+ className: seamClasses({
404
+ overlapAbove,
405
+ overlapBelow
406
+ }),
407
+ children: /* @__PURE__ */ jsxs("div", {
408
+ className: cx(containerClassName, "py-16 lg:py-24"),
409
+ children: [/* @__PURE__ */ jsx(NapBlock, {
410
+ record,
411
+ showName: block.showName ?? true,
412
+ headingLevel: block.headingLevel ?? "h2",
413
+ renderLink: renderLinkWith(linkComponent)
414
+ }), block.emitJsonLd === false ? null : /* @__PURE__ */ jsx(JsonLd, { data: record })]
415
+ })
416
+ });
417
+ }
418
+ //#endregion
419
+ //#region src/blocks/stats-band/component.tsx
420
+ /** The select only offers 2–6, but a site can edit the config; fall back to the design's four. */
421
+ function columnsFrom(value) {
422
+ const n = Number(value ?? 4);
423
+ return Number.isInteger(n) && n >= 2 && n <= 6 ? n : 4;
424
+ }
425
+ function StatsBandBlockRenderer({ block, index, isLast, overlapAbove, overlapBelow, containerClassName, linkComponent }) {
426
+ const items = (block.items ?? []).flatMap((row, i) => row.value ? [{
427
+ id: row.id ?? String(i),
428
+ value: row.value,
429
+ label: row.label ?? "",
430
+ ...row.href ? { href: row.href } : {},
431
+ ...row.wide ? { wide: true } : {}
432
+ }] : []);
433
+ if (items.length < 2) return null;
434
+ const floats = Boolean(block.overlap);
435
+ const overAbove = floats && index > 0 && !overlapAbove;
436
+ const overBelow = floats && !isLast && !overlapBelow;
437
+ return /* @__PURE__ */ jsx("section", {
438
+ className: cx("relative", floats && "z-10", seamClasses({
439
+ overlapAbove,
440
+ overlapBelow
441
+ })),
442
+ children: /* @__PURE__ */ jsx("div", {
443
+ className: cx(containerClassName, overAbove ? SEAM_PULL_UP : "pt-16 lg:pt-24", overBelow ? SEAM_PULL_DOWN : "pb-16 lg:pb-24"),
444
+ children: /* @__PURE__ */ jsx(StatsBandBlock, {
445
+ items,
446
+ columns: columnsFrom(block.columns),
447
+ tone: block.tone ?? "card",
448
+ align: block.align ?? "start",
449
+ eyebrow: !floats && block.eyebrow || void 0,
450
+ title: !floats && block.title || void 0,
451
+ description: !floats && block.description || void 0,
452
+ containerClassName: "max-w-none px-0 sm:px-0",
453
+ renderLink: renderLinkWith(linkComponent)
454
+ })
455
+ })
456
+ });
394
457
  }
395
458
  //#endregion
396
459
  //#region src/blocks/mega-menu/rem-width.ts
@@ -409,11 +472,17 @@ function remWidth(value) {
409
472
  }
410
473
  //#endregion
411
474
  //#region src/blocks/mega-menu/component.tsx
412
- function completeLink(row) {
413
- if (!row.label || !row.href) return null;
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;
414
483
  return {
415
484
  label: row.label,
416
- href: row.href,
485
+ href,
417
486
  ...row.description ? { description: row.description } : {},
418
487
  ...row.variant ? { variant: row.variant } : {},
419
488
  ...row.icon ? { icon: row.icon } : {},
@@ -424,8 +493,10 @@ function completeLink(row) {
424
493
  * The row as `MegaMenuPanel` data, or `null` when there is nothing to open:
425
494
  * no columns, or columns whose links are not yet complete. Draft saves skip
426
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.
427
498
  */
428
- function megaMenuPanelFromRow(row) {
499
+ function megaMenuPanelFromRow(row, resolve = resolveLink) {
429
500
  const panel = row.panel;
430
501
  if (!panel?.columns?.length) return null;
431
502
  const custom = Boolean(panel.customWidths);
@@ -433,7 +504,7 @@ function megaMenuPanelFromRow(row) {
433
504
  for (const column of panel.columns) {
434
505
  const sections = [];
435
506
  for (const section of column.sections ?? []) {
436
- const links = (section.links ?? []).map(completeLink).filter((link) => link !== null);
507
+ const links = (section.links ?? []).map((link) => completeLink(link, resolve)).filter((link) => link !== null);
437
508
  if (!links.length) continue;
438
509
  sections.push({
439
510
  ...section.eyebrow ? { eyebrow: section.eyebrow } : {},
@@ -449,31 +520,33 @@ function megaMenuPanelFromRow(row) {
449
520
  });
450
521
  }
451
522
  if (!columns.some((column) => column.sections.length)) return null;
452
- const overview = panel.footer?.overview;
453
- const cta = panel.footer?.cta;
523
+ const overview = footerLink(panel.footer?.overview, resolve);
524
+ const cta = footerLink(panel.footer?.cta, resolve);
454
525
  return {
455
526
  maxWidth: remWidth(panel.customMaxWidth) ?? panel.maxWidth ?? "standard",
456
527
  columns,
457
528
  footer: {
458
- ...overview?.label && overview.href ? { overview: {
459
- label: overview.label,
460
- href: overview.href,
461
- ...overview.newTab ? { newTab: true } : {}
462
- } } : {},
463
- ...cta?.label && cta.href ? { cta: {
464
- label: cta.label,
465
- href: cta.href,
466
- ...cta.newTab ? { newTab: true } : {}
467
- } } : {}
529
+ ...overview ? { overview } : {},
530
+ ...cta ? { cta } : {}
468
531
  }
469
532
  };
470
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
+ }
471
544
  /**
472
545
  * One panel, standing alone: what a live preview shows while the row is being
473
546
  * edited. A whole header goes through `headerItemsFromBlocks` instead.
474
547
  */
475
- function MegaMenuBlockRenderer({ block, linkComponent, variants, icons }) {
476
- const panel = megaMenuPanelFromRow(block);
548
+ function MegaMenuBlockRenderer({ block, linkComponent, resolveLink: resolve, variants, icons }) {
549
+ const panel = megaMenuPanelFromRow(block, resolve);
477
550
  if (!panel) return null;
478
551
  return /* @__PURE__ */ jsx(MegaMenuPanel, {
479
552
  ...panel,
@@ -491,20 +564,23 @@ function MegaMenuBlockRenderer({ block, linkComponent, variants, icons }) {
491
564
  * site added that this package does not know.
492
565
  *
493
566
  * A `navLink` row's `newTab` is not carried: `FloatingNavItem` has no such
494
- * field, because the bar renders its own links.
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.
495
570
  */
496
- function headerItemsFromBlocks(blocks, { variants, icons, isActive, linkComponent } = {}) {
571
+ function headerItemsFromBlocks(blocks, { variants, icons, isActive, linkComponent, resolveLink: resolve = resolveLink } = {}) {
497
572
  const items = [];
498
573
  for (const row of blocks) {
499
574
  if (!row.label) continue;
500
575
  if (row.blockType === "navLink") {
501
- if (!row.href) continue;
576
+ const href = resolve(row);
577
+ if (!href) continue;
502
578
  items.push({
503
579
  label: row.label,
504
- href: row.href
580
+ href
505
581
  });
506
582
  } else if (row.blockType === "megaMenu") {
507
- const panel = megaMenuPanelFromRow(row);
583
+ const panel = megaMenuPanelFromRow(row, resolve);
508
584
  if (!panel) continue;
509
585
  items.push({
510
586
  label: row.label,
@@ -521,6 +597,6 @@ function headerItemsFromBlocks(blocks, { variants, icons, isActive, linkComponen
521
597
  });
522
598
  }
523
599
  //#endregion
524
- export { DEFAULT_CONTAINER, DefaultBlockImage, DefaultBlockLink, FaqColumnsBlockRenderer, HeroBlockRenderer, MegaMenuBlockRenderer, NapBlockRenderer, ProcessStepsBlockRenderer, RenderBlocks, ShowcasePanelsBlockRenderer, TestimonialMasonryBlockRenderer, headerItemsFromBlocks, megaMenuPanelFromRow, newTabAttributes, renderLinkWith };
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 };
525
601
 
526
602
  //# sourceMappingURL=react.mjs.map