@bison-lab/payload-blocks 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Payload CMS block configs and renderers for the Bison Lab marketing blocks.
4
4
 
5
+ Full guide, with every block rendered live: <https://payload.bisonlab.ai/>
6
+
5
7
  Install it in a Payload site and editors get the same sections `@bison-lab/ui`
6
8
  ships as React props — showcase panels, process steps, FAQ columns, testimonial
7
9
  masonry, NAP — as blocks they can add to a page.
@@ -175,6 +177,51 @@ export const NextBlockLink: BlockLinkComponent = ({ newTab, ...props }) => <Link
175
177
  Without one, `DefaultBlockLink` renders a plain anchor and sets `target` and
176
178
  `rel` together whenever `newTab` is on.
177
179
 
180
+ ### The seam
181
+
182
+ One block floats across the join between two bands: the stats band with
183
+ `overlap` on. It pulls itself up over the block above and down over the block
184
+ below with negative margins, and each neighbour adds the same distance on its
185
+ own side so the band sits across the seam without covering copy. Nothing is
186
+ configured for this: `RenderBlocks` works out which rows float and hands every
187
+ renderer two booleans, `overlapAbove` and `overlapBelow`, the same kind of
188
+ derived neighbour fact as `runIndex` and `isLast`. Every package renderer's
189
+ band wrapper adds `seamClasses(props)`; a renderer never reads a neighbour's
190
+ row.
191
+
192
+ A site's own renderer does the same. The usual case is the hero, which keeps
193
+ symmetric padding until an editor drops an overlapping band under it:
194
+
195
+ ```tsx
196
+ export function SiteHeroRenderer(props: BlockRendererProps<HeroBlockData>) {
197
+ return <PageHero overlap={props.overlapBelow} … />
198
+ }
199
+ ```
200
+
201
+ Which rows float is `RenderBlocks`' `floats` prop, defaulting to
202
+ `overlapsSeam` (the package's stats band with `overlap` on). A site with a
203
+ floating block of its own composes it:
204
+
205
+ ```tsx
206
+ <RenderBlocks … floats={(row) => overlapsSeam(row) || row.blockType === 'bookingCard'} />
207
+ ```
208
+
209
+ `SEAM_PULL_UP` / `SEAM_PULL_DOWN` are the floating block's negative margins
210
+ and `OVERLAP_ABOVE_CLEARANCE` / `OVERLAP_BELOW_CLEARANCE` the matching
211
+ padding, all from `./react`, so a site block that floats uses the same
212
+ distance the neighbours clear. A floating block that is first on the page
213
+ keeps its top padding, and one that is last keeps its bottom padding rather
214
+ than pulling the footer up.
215
+
216
+ **Tailwind has to see the renderers.** The padding and margins above are
217
+ utilities in this package's output, not in `@bison-lab/ui`, so a site's
218
+ stylesheet scans both packages:
219
+
220
+ ```css
221
+ @source '../../../node_modules/@bison-lab/ui/dist';
222
+ @source '../../../node_modules/@bison-lab/payload-blocks/dist';
223
+ ```
224
+
178
225
  ## Wiring a header
179
226
 
180
227
  Two more blocks build a header rather than a page: `megaMenuBlock` and
@@ -258,6 +305,7 @@ renders nothing, and `headerItemsFromBlocks` drops it.
258
305
  | `faqColumns` | `FAQColumnsBlock` | Answers are plain text, not Lexical — see the config for why. |
259
306
  | `testimonialMasonry` | `TestimonialMasonry` | Avatars fall back to initials. |
260
307
  | `nap` | `NapBlock` + `JsonLd` | Emits schema.org `LocalBusiness`; `phoneE164` is what the `tel:` link uses. |
308
+ | `statsBand` | `StatsBandBlock` | 2–6 figures. Phones show two per row, tablets three, `columns` from `lg`. `overlap` floats it over the seam with its neighbours — see "The seam". |
261
309
  | `megaMenu` | `MegaMenuPanel` | A header block, from `megaMenuBlock({ variants, icons })`. See "Wiring a header". |
262
310
  | `navLink` | — | A header block: a plain bar item. `headerItemsFromBlocks` maps it. |
263
311
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { ArrayFieldValidation, Block, CollectionSlug, Field, GroupField, TextFieldSingleValidation, UploadField } from "payload";
1
+ import { ArrayFieldValidation, Block, CollectionSlug, Condition, Field, GroupField, TextFieldSingleValidation, UploadField } from "payload";
2
2
 
3
3
  //#region src/blocks/hero/config.d.ts
4
4
  /**
@@ -43,6 +43,17 @@ declare const TestimonialMasonryBlock: Block;
43
43
  */
44
44
  declare const NapBlock: Block;
45
45
  //#endregion
46
+ //#region src/blocks/stats-band/config.d.ts
47
+ /**
48
+ * A strip of figures: the facts a visitor should take in at a glance.
49
+ *
50
+ * The count, the order and the desktop column count are the editor's. The
51
+ * phone and tablet layouts are the library's: two per row and three per row,
52
+ * with `wide` naming the one stat that takes a full phone row when the count
53
+ * is odd.
54
+ */
55
+ declare const StatsBandBlock: Block;
56
+ //#endregion
46
57
  //#region src/blocks/mega-menu/config.d.ts
47
58
  /**
48
59
  * The blocks a header global's `items` array takes.
@@ -163,6 +174,12 @@ interface HeadingFieldsOptions {
163
174
  required?: boolean;
164
175
  /** Description shown under the eyebrow input. */
165
176
  eyebrowDescription?: string;
177
+ /**
178
+ * Show the three fields only when this holds, e.g. unless the block floats.
179
+ * Pair it with `required: false`: a hidden required field can never be
180
+ * satisfied, and the block could not publish.
181
+ */
182
+ condition?: Condition;
166
183
  }
167
184
  /**
168
185
  * The eyebrow / title / description trio every marketing band opens with.
@@ -174,7 +191,8 @@ interface HeadingFieldsOptions {
174
191
  */
175
192
  declare function headingFields({
176
193
  required,
177
- eyebrowDescription
194
+ eyebrowDescription,
195
+ condition
178
196
  }?: HeadingFieldsOptions): Field[];
179
197
  //#endregion
180
198
  //#region src/fields/min-rows.d.ts
@@ -369,6 +387,24 @@ interface TestimonialMasonryBlockData extends BlockRow<"testimonialMasonry"> {
369
387
  minItemsForFade?: number | null;
370
388
  maxVisibleRows?: number | null;
371
389
  }
390
+ interface StatsBandItemData {
391
+ value?: string | null;
392
+ label?: string | null;
393
+ href?: string | null;
394
+ wide?: boolean | null;
395
+ id?: string | null;
396
+ }
397
+ interface StatsBandBlockData extends BlockRow<"statsBand"> {
398
+ eyebrow?: string | null;
399
+ title?: string | null;
400
+ description?: string | null;
401
+ items?: StatsBandItemData[] | null;
402
+ /** A select, so the value is the digit as a string. */
403
+ columns?: ("2" | "3" | "4" | "5" | "6") | null;
404
+ tone?: ("card" | "plain") | null;
405
+ align?: ("start" | "center") | null;
406
+ overlap?: boolean | null;
407
+ }
372
408
  interface NapDepartmentData {
373
409
  name?: string | null;
374
410
  departmentType?: string | null;
@@ -430,7 +466,7 @@ interface MegaMenuBlockData extends BlockRow<"megaMenu"> {
430
466
  }
431
467
  interface NavLinkBlockData extends BlockRow<"navLink">, LinkValue {}
432
468
  /** Every block this package ships, as one union. */
433
- type BisonBlockData = HeroBlockData | RichTextBlockData | ShowcasePanelsBlockData | ProcessStepsBlockData | FaqColumnsBlockData | TestimonialMasonryBlockData | NapBlockData | MegaMenuBlockData | NavLinkBlockData;
469
+ type BisonBlockData = HeroBlockData | RichTextBlockData | ShowcasePanelsBlockData | ProcessStepsBlockData | FaqColumnsBlockData | TestimonialMasonryBlockData | NapBlockData | StatsBandBlockData | MegaMenuBlockData | NavLinkBlockData;
434
470
  /** The `blockType` of every block this package ships. */
435
471
  type BisonBlockType = BisonBlockData["blockType"];
436
472
  //#endregion
@@ -505,5 +541,5 @@ declare function sampleImage({
505
541
  alt
506
542
  }: SampleImageOptions): MediaDoc;
507
543
  //#endregion
508
- export { type BisonBlockData, type BisonBlockType, type BlockSamples, FaqColumnsBlock, type FaqColumnsBlockData, type FaqColumnsItemData, type HeadingFieldsOptions, HeroBlock, type HeroBlockData, type ImageFieldOptions, LinkBlock, type LinkFieldOptions, type LinkFieldsOptions, type LinkValue, MEGA_MENU_WIDTHS, MIN_ROWS_ARRAY_FIELD, type MediaDoc, type MediaValue, type MegaMenuBlockData, type MegaMenuBlockOptions, type MegaMenuColumnData, type MegaMenuLinkData, type MegaMenuOption, type MegaMenuSectionData, type MegaMenuWidthValue, NapBlock, type NapBlockData, type NapDepartmentData, type NavLinkBlockData, ProcessStepsBlock, type ProcessStepsBlockData, type ProcessStepsItemData, type ResolvedMedia, RichTextBlock, type RichTextBlockData, type RichTextContent, type SampleImageOptions, ShowcasePanelsBlock, type ShowcasePanelsBlockData, type ShowcasePanelsItemData, TestimonialMasonryBlock, type TestimonialMasonryBlockData, type TestimonialMasonryItemData, blockSamples, emptyRows, headingFields, imageField, linkField, linkFields, megaMenuBlock, megaMenuSampleOptions, resolveMedia, sampleImage, validateColumnWidths, validateRemWidth };
544
+ export { type BisonBlockData, type BisonBlockType, type BlockSamples, FaqColumnsBlock, type FaqColumnsBlockData, type FaqColumnsItemData, type HeadingFieldsOptions, HeroBlock, type HeroBlockData, type ImageFieldOptions, LinkBlock, type LinkFieldOptions, type LinkFieldsOptions, type LinkValue, MEGA_MENU_WIDTHS, MIN_ROWS_ARRAY_FIELD, type MediaDoc, type MediaValue, type MegaMenuBlockData, type MegaMenuBlockOptions, type MegaMenuColumnData, type MegaMenuLinkData, type MegaMenuOption, type MegaMenuSectionData, type MegaMenuWidthValue, NapBlock, type NapBlockData, type NapDepartmentData, type NavLinkBlockData, ProcessStepsBlock, type ProcessStepsBlockData, type ProcessStepsItemData, type ResolvedMedia, RichTextBlock, type RichTextBlockData, type RichTextContent, type SampleImageOptions, ShowcasePanelsBlock, type ShowcasePanelsBlockData, type ShowcasePanelsItemData, StatsBandBlock, type StatsBandBlockData, type StatsBandItemData, TestimonialMasonryBlock, type TestimonialMasonryBlockData, type TestimonialMasonryItemData, blockSamples, emptyRows, headingFields, imageField, linkField, linkFields, megaMenuBlock, megaMenuSampleOptions, resolveMedia, sampleImage, validateColumnWidths, validateRemWidth };
509
545
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/blocks/hero/config.ts","../src/blocks/rich-text/config.ts","../src/blocks/showcase-panels/config.ts","../src/blocks/process-steps/config.ts","../src/blocks/faq-columns/config.ts","../src/blocks/testimonial-masonry/config.ts","../src/blocks/nap/config.ts","../src/blocks/mega-menu/config.ts","../src/fields/image.ts","../src/fields/link.ts","../src/fields/heading.ts","../src/fields/min-rows.ts","../src/media.ts","../src/types.ts","../src/samples.ts","../src/blocks/mega-menu/sample.ts","../src/sample-image.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/blocks/hero/config.ts","../src/blocks/rich-text/config.ts","../src/blocks/showcase-panels/config.ts","../src/blocks/process-steps/config.ts","../src/blocks/faq-columns/config.ts","../src/blocks/testimonial-masonry/config.ts","../src/blocks/nap/config.ts","../src/blocks/stats-band/config.ts","../src/blocks/mega-menu/config.ts","../src/fields/image.ts","../src/fields/link.ts","../src/fields/heading.ts","../src/fields/min-rows.ts","../src/media.ts","../src/types.ts","../src/samples.ts","../src/blocks/mega-menu/sample.ts","../src/sample-image.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -323,21 +323,27 @@ const ProcessStepsBlock = {
323
323
  * puts the least-optional copy behind a click. The field names match the
324
324
  * `@bison-lab/ui` prop names so the renderers stay a pass-through.
325
325
  */
326
- function headingFields({ required = true, eyebrowDescription = "Small label above the heading. Leave empty to hide the row." } = {}) {
326
+ function headingFields({ required = true, eyebrowDescription = "Small label above the heading. Leave empty to hide the row.", condition } = {}) {
327
+ const when = condition ? { condition } : {};
327
328
  return [
328
329
  {
329
330
  name: "eyebrow",
330
331
  type: "text",
331
- admin: { description: eyebrowDescription }
332
+ admin: {
333
+ description: eyebrowDescription,
334
+ ...when
335
+ }
332
336
  },
333
337
  {
334
338
  name: "title",
335
339
  type: "text",
336
- required
340
+ required,
341
+ admin: when
337
342
  },
338
343
  {
339
344
  name: "description",
340
- type: "textarea"
345
+ type: "textarea",
346
+ admin: when
341
347
  }
342
348
  ];
343
349
  }
@@ -641,6 +647,177 @@ const NapBlock = {
641
647
  ]
642
648
  };
643
649
  //#endregion
650
+ //#region src/fields/row-limits.ts
651
+ /**
652
+ * A custom `validate` replaces Payload's stock array check, so any array
653
+ * with its own rule re-applies the row limits first or `minRows` and
654
+ * `maxRows` stop being enforced. Same messages as the stock check, through
655
+ * the request's translator when the admin supplies one.
656
+ */
657
+ function rowLimits(value, { minRows, maxRows, required, req }) {
658
+ const count = value?.length ?? 0;
659
+ const t = req?.t;
660
+ if (required && count === 0) return t ? t("validation:required") : "This field is required.";
661
+ if (minRows && count < minRows) return t ? t("validation:requiresAtLeast", {
662
+ count: minRows,
663
+ label: t("general:rows")
664
+ }) : `This field requires at least ${minRows} rows.`;
665
+ if (maxRows && count > maxRows) return t ? t("validation:requiresNoMoreThan", {
666
+ count: maxRows,
667
+ label: t("general:rows")
668
+ }) : `This field requires no more than ${maxRows} rows.`;
669
+ return true;
670
+ }
671
+ //#endregion
672
+ //#region src/blocks/stats-band/config.ts
673
+ /**
674
+ * The rule on `wide`: on phones the band is two per row, so only an odd
675
+ * count has a stat on a row of its own, only one stat can take it, and it
676
+ * has to be a stat that starts a row (the 1st, 3rd or 5th), since a full-row
677
+ * cell beside another cell would leave a hole. The array's own `validate`,
678
+ * so the editor sees the message as they build, and Payload runs it again on
679
+ * publish.
680
+ */
681
+ const validateWideFlag = (value, options) => {
682
+ const limits = rowLimits(value, options);
683
+ if (limits !== true) return limits;
684
+ const rows = value ?? [];
685
+ const wide = rows.flatMap((row, i) => row?.wide ? [i] : []);
686
+ if (wide.length > 1) return "Only one stat can be full width on phones.";
687
+ if (wide.length === 0) return true;
688
+ if (rows.length % 2 === 0) return "Full width on phones only applies to an odd number of stats. With an even count every row is already full.";
689
+ if (wide[0] % 2 !== 0) return "Only a stat that starts a phone row can be full width: the 1st, 3rd or 5th. Move it up or down one place, or flag another.";
690
+ return true;
691
+ };
692
+ /**
693
+ * An overlapping band is headless: it floats across the seam as a card, and a
694
+ * heading pulled up into the block above would land on the wrong surface.
695
+ * The heading fields disappear from the admin the moment `overlap` is on,
696
+ * and the renderer ignores them if they were filled in first.
697
+ */
698
+ const unlessOverlap = (_data, siblingData) => !siblingData?.overlap;
699
+ /**
700
+ * A strip of figures: the facts a visitor should take in at a glance.
701
+ *
702
+ * The count, the order and the desktop column count are the editor's. The
703
+ * phone and tablet layouts are the library's: two per row and three per row,
704
+ * with `wide` naming the one stat that takes a full phone row when the count
705
+ * is odd.
706
+ */
707
+ const StatsBandBlock = {
708
+ slug: "statsBand",
709
+ interfaceName: "StatsBandBlock",
710
+ labels: {
711
+ singular: "Stats Band",
712
+ plural: "Stats Bands"
713
+ },
714
+ fields: [
715
+ ...headingFields({
716
+ required: false,
717
+ eyebrowDescription: "Small label above the heading. The heading and eyebrow are optional; without them the band stands alone.",
718
+ condition: unlessOverlap
719
+ }),
720
+ {
721
+ name: "items",
722
+ type: "array",
723
+ required: true,
724
+ minRows: 2,
725
+ maxRows: 6,
726
+ defaultValue: emptyRows(2),
727
+ labels: {
728
+ singular: "Stat",
729
+ plural: "Stats"
730
+ },
731
+ admin: { components: { Field: MIN_ROWS_ARRAY_FIELD } },
732
+ validate: validateWideFlag,
733
+ fields: [
734
+ {
735
+ name: "value",
736
+ type: "text",
737
+ required: true,
738
+ admin: { description: "The figure, as it should read: \"98%\", \"12,000+\", \"L1–S1\"." }
739
+ },
740
+ {
741
+ name: "label",
742
+ type: "textarea",
743
+ required: true
744
+ },
745
+ {
746
+ name: "href",
747
+ type: "text",
748
+ admin: { description: "Optional. Makes the whole stat a link: a site path (\"/outcomes\") or a full URL." }
749
+ },
750
+ {
751
+ name: "wide",
752
+ type: "checkbox",
753
+ label: "Full width on phones",
754
+ defaultValue: false,
755
+ admin: { description: "Phones show two per row. With an odd number of stats, this one takes a row to itself. Only the 1st, 3rd or 5th stat can." }
756
+ }
757
+ ]
758
+ },
759
+ {
760
+ name: "columns",
761
+ type: "select",
762
+ defaultValue: "4",
763
+ options: [
764
+ {
765
+ label: "2",
766
+ value: "2"
767
+ },
768
+ {
769
+ label: "3",
770
+ value: "3"
771
+ },
772
+ {
773
+ label: "4",
774
+ value: "4"
775
+ },
776
+ {
777
+ label: "5",
778
+ value: "5"
779
+ },
780
+ {
781
+ label: "6",
782
+ value: "6"
783
+ }
784
+ ],
785
+ admin: { description: "From large screens up, and never more than there are stats. Phones show two per row and tablets three." }
786
+ },
787
+ {
788
+ name: "tone",
789
+ type: "select",
790
+ defaultValue: "card",
791
+ options: [{
792
+ label: "Card (bordered, raised)",
793
+ value: "card"
794
+ }, {
795
+ label: "Plain (dividers only)",
796
+ value: "plain"
797
+ }]
798
+ },
799
+ {
800
+ name: "align",
801
+ type: "select",
802
+ defaultValue: "start",
803
+ options: [{
804
+ label: "Left",
805
+ value: "start"
806
+ }, {
807
+ label: "Centred",
808
+ value: "center"
809
+ }]
810
+ },
811
+ {
812
+ name: "overlap",
813
+ type: "checkbox",
814
+ label: "Float over the seam with the block above",
815
+ defaultValue: false,
816
+ admin: { description: "The band sits across the join between the block above and the block below. Both make room for it automatically. A floating band is headless: the heading fields above are hidden while this is on." }
817
+ }
818
+ ]
819
+ };
820
+ //#endregion
644
821
  //#region src/blocks/mega-menu/rem-width.ts
645
822
  /**
646
823
  * The one rule for a typed panel width, shared by the config's validator and
@@ -676,25 +853,6 @@ const TWELFTHS = {
676
853
  "100": 12
677
854
  };
678
855
  /**
679
- * A custom `validate` replaces Payload's stock array check, so the row limits
680
- * are re-applied here or five columns would pass. Same messages as the stock
681
- * check, through the request's translator when the admin supplies one.
682
- */
683
- function rowLimits(value, { minRows, maxRows, required, req }) {
684
- const count = value?.length ?? 0;
685
- const t = req?.t;
686
- if (required && count === 0) return t ? t("validation:required") : "This field is required.";
687
- if (minRows && count < minRows) return t ? t("validation:requiresAtLeast", {
688
- count: minRows,
689
- label: t("general:rows")
690
- }) : `This field requires at least ${minRows} rows.`;
691
- if (maxRows && count > maxRows) return t ? t("validation:requiresNoMoreThan", {
692
- count: maxRows,
693
- label: t("general:rows")
694
- }) : `This field requires no more than ${maxRows} rows.`;
695
- return true;
696
- }
697
- /**
698
856
  * The rule on `columns`: read as fractions, the widths must total 100%. It is
699
857
  * the array's own `validate`, so the editor sees the message as they build,
700
858
  * and Payload runs field validation again on publish (only draft saves skip
@@ -1422,6 +1580,50 @@ const showcasePanelsSample = {
1422
1580
  defaultActiveIndex: 0
1423
1581
  };
1424
1582
  //#endregion
1583
+ //#region src/blocks/stats-band/sample.ts
1584
+ /**
1585
+ * Four figures, the count the strip was designed around: one row on a
1586
+ * desktop, two by two on a tablet and a phone. `wide` is set (to `false`) so
1587
+ * the preview exercises the field, and an even count is where it is ignored.
1588
+ * `overlap` is off so the heading shows: a floating band is headless, and a
1589
+ * preview page has no hero for it to float over.
1590
+ */
1591
+ const statsBandSample = {
1592
+ id: "sample-stats-band",
1593
+ blockType: "statsBand",
1594
+ eyebrow: "At a glance",
1595
+ title: "The clinic in numbers",
1596
+ description: "What a year looks like across our two rooms, counted from the front desk rather than estimated.",
1597
+ items: [
1598
+ {
1599
+ id: "sample-stat-pain",
1600
+ value: "94%",
1601
+ label: "of patients report less pain by their sixth session.",
1602
+ href: "/outcomes",
1603
+ wide: false
1604
+ },
1605
+ {
1606
+ id: "sample-stat-visits",
1607
+ value: "4,200+",
1608
+ label: "appointments a year across two treatment rooms."
1609
+ },
1610
+ {
1611
+ id: "sample-stat-wait",
1612
+ value: "15 min",
1613
+ label: "average wait from the front desk to the treatment room."
1614
+ },
1615
+ {
1616
+ id: "sample-stat-years",
1617
+ value: "12",
1618
+ label: "years in the same building, on the same street."
1619
+ }
1620
+ ],
1621
+ columns: "4",
1622
+ tone: "card",
1623
+ align: "start",
1624
+ overlap: false
1625
+ };
1626
+ //#endregion
1425
1627
  //#region src/blocks/testimonial-masonry/sample.ts
1426
1628
  function avatar(name) {
1427
1629
  return sampleImage({
@@ -1520,10 +1722,11 @@ const blockSamples = {
1520
1722
  maxVisibleRows: 2
1521
1723
  },
1522
1724
  nap: napSample,
1725
+ statsBand: statsBandSample,
1523
1726
  megaMenu: megaMenuSample,
1524
1727
  navLink: navLinkSample
1525
1728
  };
1526
1729
  //#endregion
1527
- export { FaqColumnsBlock, HeroBlock, LinkBlock, MEGA_MENU_WIDTHS, MIN_ROWS_ARRAY_FIELD, NapBlock, ProcessStepsBlock, RichTextBlock, ShowcasePanelsBlock, TestimonialMasonryBlock, blockSamples, emptyRows, headingFields, imageField, linkField, linkFields, megaMenuBlock, megaMenuSampleOptions, resolveMedia, sampleImage, validateColumnWidths, validateRemWidth };
1730
+ export { FaqColumnsBlock, HeroBlock, LinkBlock, MEGA_MENU_WIDTHS, MIN_ROWS_ARRAY_FIELD, NapBlock, ProcessStepsBlock, RichTextBlock, ShowcasePanelsBlock, StatsBandBlock, TestimonialMasonryBlock, blockSamples, emptyRows, headingFields, imageField, linkField, linkFields, megaMenuBlock, megaMenuSampleOptions, resolveMedia, sampleImage, validateColumnWidths, validateRemWidth };
1528
1731
 
1529
1732
  //# sourceMappingURL=index.mjs.map