@bison-lab/payload-blocks 3.0.0 → 3.1.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 +44 -0
- package/dist/index.d.mts +55 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +433 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -172,6 +172,44 @@ A row whose upload has not resolved is dropped rather than rendered empty, and a
|
|
|
172
172
|
block left with nothing to show renders nothing at all. Draft saves skip Payload
|
|
173
173
|
validation, so live preview genuinely hands renderers incomplete rows.
|
|
174
174
|
|
|
175
|
+
## Samples
|
|
176
|
+
|
|
177
|
+
Every block ships a sample row, so it can be rendered with no CMS behind it:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { blockSamples } from '@bison-lab/payload-blocks'
|
|
181
|
+
|
|
182
|
+
blockSamples.showcasePanels // a ShowcasePanelsBlockData with three panels
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`blockSamples` is one map keyed by `blockType`, typed per block
|
|
186
|
+
(`BlockSamples`) and assignable to `Record<BisonBlockType, BisonBlockData>`.
|
|
187
|
+
It comes from the main entry and is React-free, so a Global's field config and
|
|
188
|
+
a route handler can both read it. The Block library page that renders these
|
|
189
|
+
live for an admin is a separate piece of work (BIS-52); this package ships the
|
|
190
|
+
data, not the page.
|
|
191
|
+
|
|
192
|
+
Each sample has realistic copy at the length the layout was designed for
|
|
193
|
+
(three showcase panels, four process steps, six testimonials), and sets every
|
|
194
|
+
field its config has at least once, so a preview shows the block's full range.
|
|
195
|
+
|
|
196
|
+
**Images travel with the sample.** No site media exists in a preview, so every
|
|
197
|
+
upload in a sample is an inline SVG `data:` URL in the `MediaDoc` shape, with
|
|
198
|
+
`width` and `height` set. `resolveMedia` accepts it as-is, and the `data:`
|
|
199
|
+
scheme is how an adapter knows what it has: `next/image` marks a `data:` src
|
|
200
|
+
`unoptimized` by itself, so the adapter above needs no special case. A site
|
|
201
|
+
that wants the same placeholders for its own blocks can build them with
|
|
202
|
+
`sampleImage({ label, width, height, alt })`.
|
|
203
|
+
|
|
204
|
+
The `nap` sample sets `emitJsonLd: false`. The block emits schema.org
|
|
205
|
+
`LocalBusiness` by default, and a preview must not put a fictional clinic into
|
|
206
|
+
a site's structured data.
|
|
207
|
+
|
|
208
|
+
Only what this package ships has a sample here. A site's own blocks supply
|
|
209
|
+
theirs through the Block library factory, whose option BIS-52 names. The hero
|
|
210
|
+
family (BIS-45) adds a sample per hero as each one lands; the plain `hero`
|
|
211
|
+
renderer's is here.
|
|
212
|
+
|
|
175
213
|
## Conventions for contributors
|
|
176
214
|
|
|
177
215
|
- **No generated types.** `src/types.ts` hand-writes each block's row shape to
|
|
@@ -192,3 +230,9 @@ validation, so live preview genuinely hands renderers incomplete rows.
|
|
|
192
230
|
- **Changing a field is a schema change in every consuming site.** Update
|
|
193
231
|
`src/types.ts` and the field-name lock in `src/__tests__/configs.test.ts` in
|
|
194
232
|
the same change, and say "run `payload migrate:create`" in the changeset.
|
|
233
|
+
- **Every block ships a sample.** A new block adds `src/blocks/<slug>/sample.ts`
|
|
234
|
+
beside its `config.ts` and `component.tsx`, typed to its row shape, using
|
|
235
|
+
`sampleImage` for every upload, and registers it in `src/samples.ts`.
|
|
236
|
+
`src/__tests__/samples.test.tsx` fails until it does, and again if the sample
|
|
237
|
+
leaves a config field unset. A new field on an existing block means its
|
|
238
|
+
sample sets that field too.
|
package/dist/index.d.mts
CHANGED
|
@@ -352,5 +352,59 @@ type BisonBlockData = HeroBlockData | RichTextBlockData | ShowcasePanelsBlockDat
|
|
|
352
352
|
/** The `blockType` of every block this package ships. */
|
|
353
353
|
type BisonBlockType = BisonBlockData["blockType"];
|
|
354
354
|
//#endregion
|
|
355
|
-
|
|
355
|
+
//#region src/samples.d.ts
|
|
356
|
+
/**
|
|
357
|
+
* One sample row per block, each typed to its own row shape. Assignable to
|
|
358
|
+
* `Record<BisonBlockType, BisonBlockData>` wherever the wider type is wanted.
|
|
359
|
+
*/
|
|
360
|
+
type BlockSamples = { [K in BisonBlockType]: Extract<BisonBlockData, {
|
|
361
|
+
blockType: K;
|
|
362
|
+
}> };
|
|
363
|
+
/**
|
|
364
|
+
* Every block the package ships, rendered without a CMS row.
|
|
365
|
+
*
|
|
366
|
+
* A Block library page (BIS-52) renders these live so an admin can see each
|
|
367
|
+
* block before enabling it. Each sample lives beside its block's `config.ts`
|
|
368
|
+
* and `component.tsx` as `sample.ts`, and `src/__tests__/samples.test.tsx`
|
|
369
|
+
* locks the contract: one entry per slug, `blockType` matching the key, every
|
|
370
|
+
* config field exercised at least once, and every image self-contained.
|
|
371
|
+
*
|
|
372
|
+
* React-free on purpose: it is read from a Global's field config, which
|
|
373
|
+
* Payload loads in plain Node like the rest of this entry, as well as from a
|
|
374
|
+
* route handler. Only what this package ships is here; a site supplies samples
|
|
375
|
+
* for its own blocks through the Block library factory, whose option BIS-52
|
|
376
|
+
* names.
|
|
377
|
+
*/
|
|
378
|
+
declare const blockSamples: BlockSamples;
|
|
379
|
+
//#endregion
|
|
380
|
+
//#region src/sample-image.d.ts
|
|
381
|
+
interface SampleImageOptions {
|
|
382
|
+
/** Text drawn across the placeholder, e.g. "Panel 1 · 1400 × 1000". */
|
|
383
|
+
label: string;
|
|
384
|
+
width: number;
|
|
385
|
+
height: number;
|
|
386
|
+
/** Alt text for the document. Defaults to the label. */
|
|
387
|
+
alt?: string;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* A placeholder upload document for a block sample.
|
|
391
|
+
*
|
|
392
|
+
* A preview has no site media behind it, so the image has to travel with the
|
|
393
|
+
* sample. This is an inline SVG as a `data:` URL, in the `MediaDoc` shape
|
|
394
|
+
* `resolveMedia` already narrows, with `width` and `height` set so an image
|
|
395
|
+
* adapter can reserve the box. The scheme is what tells an adapter which kind
|
|
396
|
+
* of source it has: `next/image` treats a `data:` src as `unoptimized` on its
|
|
397
|
+
* own, so a site's adapter needs no special case for samples.
|
|
398
|
+
*
|
|
399
|
+
* Neutral greys rather than theme tokens: the SVG is a standalone document
|
|
400
|
+
* and cannot see the page's custom properties.
|
|
401
|
+
*/
|
|
402
|
+
declare function sampleImage({
|
|
403
|
+
label,
|
|
404
|
+
width,
|
|
405
|
+
height,
|
|
406
|
+
alt
|
|
407
|
+
}: SampleImageOptions): MediaDoc;
|
|
408
|
+
//#endregion
|
|
409
|
+
export { type BisonBlockData, type BisonBlockType, type BlockSamples, FaqColumnsBlock, type FaqColumnsBlockData, type FaqColumnsItemData, type HeadingFieldsOptions, HeroBlock, type HeroBlockData, type ImageFieldOptions, type LinkFieldOptions, type LinkFieldsOptions, type LinkValue, MIN_ROWS_ARRAY_FIELD, type MediaDoc, type MediaValue, NapBlock, type NapBlockData, type NapDepartmentData, 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, resolveMedia, sampleImage };
|
|
356
410
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -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/fields/image.ts","../src/fields/link.ts","../src/fields/heading.ts","../src/fields/min-rows.ts","../src/media.ts","../src/types.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/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/sample-image.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -669,6 +669,438 @@ function resolveMedia(value) {
|
|
|
669
669
|
};
|
|
670
670
|
}
|
|
671
671
|
//#endregion
|
|
672
|
-
|
|
672
|
+
//#region src/blocks/faq-columns/sample.ts
|
|
673
|
+
const faqColumnsSample = {
|
|
674
|
+
id: "sample-faq-columns",
|
|
675
|
+
blockType: "faqColumns",
|
|
676
|
+
eyebrow: "Good to know",
|
|
677
|
+
title: "Questions patients ask before their first visit",
|
|
678
|
+
description: "If yours is not here, the front desk answers the phone between eight and six on weekdays.",
|
|
679
|
+
items: [
|
|
680
|
+
{
|
|
681
|
+
id: "sample-faq-referral",
|
|
682
|
+
question: "Do I need a referral?",
|
|
683
|
+
answer: "No. You can book directly. Some insurers ask for one before they reimburse, so check your policy if you plan to claim."
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
id: "sample-faq-sessions",
|
|
687
|
+
question: "How many sessions will I need?",
|
|
688
|
+
answer: "Most plans finish in six to eight sessions. You will get a written estimate after your assessment, and we revise it with you as you progress."
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
id: "sample-faq-wear",
|
|
692
|
+
question: "What should I wear?",
|
|
693
|
+
answer: "Anything you can move in. Shorts for a knee or hip, a vest or loose top for a shoulder or neck.\n\nThere are changing rooms if you are coming from work."
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
id: "sample-faq-insurance",
|
|
697
|
+
question: "Is treatment covered by insurance?",
|
|
698
|
+
answer: "Usually, once conservative treatment has been recommended. We invoice you directly and give you everything the insurer needs to reimburse you."
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
id: "sample-faq-cancel",
|
|
702
|
+
question: "What is the cancellation policy?",
|
|
703
|
+
answer: "Twenty-four hours' notice, by phone or through the booking link in your confirmation email. Later than that and the session is charged."
|
|
704
|
+
}
|
|
705
|
+
],
|
|
706
|
+
cta: {
|
|
707
|
+
title: "Still have questions?",
|
|
708
|
+
linkText: "Call the front desk",
|
|
709
|
+
href: "tel:+13035550142",
|
|
710
|
+
newTab: false
|
|
711
|
+
},
|
|
712
|
+
sticky: true,
|
|
713
|
+
type: "single",
|
|
714
|
+
collapsible: true
|
|
715
|
+
};
|
|
716
|
+
//#endregion
|
|
717
|
+
//#region src/sample-image.ts
|
|
718
|
+
function escapeXml(value) {
|
|
719
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* A placeholder upload document for a block sample.
|
|
723
|
+
*
|
|
724
|
+
* A preview has no site media behind it, so the image has to travel with the
|
|
725
|
+
* sample. This is an inline SVG as a `data:` URL, in the `MediaDoc` shape
|
|
726
|
+
* `resolveMedia` already narrows, with `width` and `height` set so an image
|
|
727
|
+
* adapter can reserve the box. The scheme is what tells an adapter which kind
|
|
728
|
+
* of source it has: `next/image` treats a `data:` src as `unoptimized` on its
|
|
729
|
+
* own, so a site's adapter needs no special case for samples.
|
|
730
|
+
*
|
|
731
|
+
* Neutral greys rather than theme tokens: the SVG is a standalone document
|
|
732
|
+
* and cannot see the page's custom properties.
|
|
733
|
+
*/
|
|
734
|
+
function sampleImage({ label, width, height, alt }) {
|
|
735
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><rect width="100%" height="100%" fill="#d4d4d8"/><text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-family="system-ui, sans-serif" font-size="${Math.max(12, Math.round(Math.min(width, height) / 12))}" fill="#52525b">${escapeXml(label)}</text></svg>`;
|
|
736
|
+
return {
|
|
737
|
+
url: `data:image/svg+xml,${encodeURIComponent(svg)}`,
|
|
738
|
+
alt: alt ?? label,
|
|
739
|
+
width,
|
|
740
|
+
height
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
//#endregion
|
|
744
|
+
//#region src/blocks/hero/sample.ts
|
|
745
|
+
/**
|
|
746
|
+
* The placeholder hero's sample. The hero family (BIS-45) ships a sample per
|
|
747
|
+
* hero as each one is added; this is the one for the plain renderer.
|
|
748
|
+
*/
|
|
749
|
+
const heroSample = {
|
|
750
|
+
id: "sample-hero",
|
|
751
|
+
blockType: "hero",
|
|
752
|
+
eyebrow: "Now booking spring appointments",
|
|
753
|
+
heading: "Move well again, without the long wait",
|
|
754
|
+
body: "Same-week assessments with a physiotherapist who stays with you from the first visit to the last. Most treatment plans finish in six to eight sessions.",
|
|
755
|
+
tone: "sweep",
|
|
756
|
+
image: sampleImage({
|
|
757
|
+
label: "Hero background · 2000 × 1000",
|
|
758
|
+
width: 2e3,
|
|
759
|
+
height: 1e3,
|
|
760
|
+
alt: "A bright, open treatment room"
|
|
761
|
+
}),
|
|
762
|
+
links: [{
|
|
763
|
+
label: "Book an assessment",
|
|
764
|
+
href: "/book",
|
|
765
|
+
newTab: false
|
|
766
|
+
}, {
|
|
767
|
+
label: "See our approach",
|
|
768
|
+
href: "https://example.com/approach",
|
|
769
|
+
newTab: true
|
|
770
|
+
}]
|
|
771
|
+
};
|
|
772
|
+
//#endregion
|
|
773
|
+
//#region src/blocks/nap/sample.ts
|
|
774
|
+
/**
|
|
775
|
+
* A fictional clinic. `emitJsonLd` is off: the block emits schema.org
|
|
776
|
+
* `LocalBusiness` by default, and a preview must not put a business that does
|
|
777
|
+
* not exist into a site's structured data. The phone numbers are in the
|
|
778
|
+
* 555-01xx range reserved for fiction.
|
|
779
|
+
*/
|
|
780
|
+
const napSample = {
|
|
781
|
+
id: "sample-nap",
|
|
782
|
+
blockType: "nap",
|
|
783
|
+
businessName: "Larkspur Physiotherapy",
|
|
784
|
+
businessType: "Physiotherapy",
|
|
785
|
+
address: {
|
|
786
|
+
streetAddress: "400 Larkspur Lane, Suite 120",
|
|
787
|
+
addressLocality: "Boulder",
|
|
788
|
+
addressRegion: "CO",
|
|
789
|
+
postalCode: "80302",
|
|
790
|
+
addressCountry: "US"
|
|
791
|
+
},
|
|
792
|
+
departments: [{
|
|
793
|
+
id: "sample-dept-front-desk",
|
|
794
|
+
name: "Front desk",
|
|
795
|
+
phoneE164: "+13035550142",
|
|
796
|
+
phoneDisplay: "(303) 555-0142"
|
|
797
|
+
}, {
|
|
798
|
+
id: "sample-dept-billing",
|
|
799
|
+
name: "Billing and insurance",
|
|
800
|
+
departmentType: "AccountingService",
|
|
801
|
+
phoneE164: "+13035550143"
|
|
802
|
+
}],
|
|
803
|
+
url: "https://example.com",
|
|
804
|
+
showName: true,
|
|
805
|
+
headingLevel: "h2",
|
|
806
|
+
emitJsonLd: false
|
|
807
|
+
};
|
|
808
|
+
//#endregion
|
|
809
|
+
//#region src/blocks/process-steps/sample.ts
|
|
810
|
+
/** Four steps: enough to show the rail advancing through a real sequence. */
|
|
811
|
+
const processStepsSample = {
|
|
812
|
+
id: "sample-process-steps",
|
|
813
|
+
blockType: "processSteps",
|
|
814
|
+
items: [
|
|
815
|
+
{
|
|
816
|
+
id: "sample-step-assess",
|
|
817
|
+
title: "Assessment",
|
|
818
|
+
description: "A fifty-minute first visit: your history, a movement screen, and a clear explanation of what we found.",
|
|
819
|
+
image: sampleImage({
|
|
820
|
+
label: "Step 1 · 1600 × 1000",
|
|
821
|
+
width: 1600,
|
|
822
|
+
height: 1e3,
|
|
823
|
+
alt: "A physiotherapist taking notes during an assessment"
|
|
824
|
+
})
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
id: "sample-step-plan",
|
|
828
|
+
title: "Your plan",
|
|
829
|
+
description: "A written plan with the number of sessions we expect, what each one is for, and the exercises between them.",
|
|
830
|
+
image: sampleImage({
|
|
831
|
+
label: "Step 2 · 1600 × 1000",
|
|
832
|
+
width: 1600,
|
|
833
|
+
height: 1e3,
|
|
834
|
+
alt: "A printed treatment plan on a desk"
|
|
835
|
+
})
|
|
836
|
+
},
|
|
837
|
+
{
|
|
838
|
+
id: "sample-step-treat",
|
|
839
|
+
title: "Treatment",
|
|
840
|
+
description: "Hands-on work where it helps, and progressive loading where it matters. You leave every session knowing what to do next.",
|
|
841
|
+
image: sampleImage({
|
|
842
|
+
label: "Step 3 · 1600 × 1000",
|
|
843
|
+
width: 1600,
|
|
844
|
+
height: 1e3,
|
|
845
|
+
alt: "A patient lifting a light kettlebell under supervision"
|
|
846
|
+
})
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
id: "sample-step-discharge",
|
|
850
|
+
title: "Discharge and beyond",
|
|
851
|
+
description: "A final review, a maintenance programme, and an open door if anything flares up later.",
|
|
852
|
+
image: sampleImage({
|
|
853
|
+
label: "Step 4 · 1600 × 1000",
|
|
854
|
+
width: 1600,
|
|
855
|
+
height: 1e3,
|
|
856
|
+
alt: "A patient walking out of the clinic"
|
|
857
|
+
})
|
|
858
|
+
}
|
|
859
|
+
],
|
|
860
|
+
autoAdvance: true,
|
|
861
|
+
autoAdvanceDuration: 5e3,
|
|
862
|
+
pauseOnHover: true,
|
|
863
|
+
defaultActiveIndex: 0
|
|
864
|
+
};
|
|
865
|
+
//#endregion
|
|
866
|
+
//#region src/blocks/rich-text/sample.ts
|
|
867
|
+
/**
|
|
868
|
+
* A serialized Lexical document, written by hand in the shapes the default
|
|
869
|
+
* JSX converters read (`heading`, `paragraph`, `text`, `list`, `link`).
|
|
870
|
+
* `version` is on every serialized Lexical node, and `direction`, `format` and
|
|
871
|
+
* `indent` are what `RichTextContent` requires on the root; the converters
|
|
872
|
+
* read none of them, and the sample carries them so it is shaped like a row
|
|
873
|
+
* the editor saved.
|
|
874
|
+
*/
|
|
875
|
+
const block = {
|
|
876
|
+
direction: "ltr",
|
|
877
|
+
format: "",
|
|
878
|
+
indent: 0,
|
|
879
|
+
version: 1
|
|
880
|
+
};
|
|
881
|
+
function text(value, format = 0) {
|
|
882
|
+
return {
|
|
883
|
+
type: "text",
|
|
884
|
+
text: value,
|
|
885
|
+
format,
|
|
886
|
+
detail: 0,
|
|
887
|
+
mode: "normal",
|
|
888
|
+
style: "",
|
|
889
|
+
version: 1
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
function paragraph(children) {
|
|
893
|
+
return {
|
|
894
|
+
type: "paragraph",
|
|
895
|
+
children,
|
|
896
|
+
textFormat: 0,
|
|
897
|
+
textStyle: "",
|
|
898
|
+
...block
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
const BOLD = 1;
|
|
902
|
+
const richTextSample = {
|
|
903
|
+
id: "sample-rich-text",
|
|
904
|
+
blockType: "richText",
|
|
905
|
+
content: { root: {
|
|
906
|
+
type: "root",
|
|
907
|
+
...block,
|
|
908
|
+
children: [
|
|
909
|
+
{
|
|
910
|
+
type: "heading",
|
|
911
|
+
tag: "h2",
|
|
912
|
+
children: [text("What to expect at your first visit")],
|
|
913
|
+
...block
|
|
914
|
+
},
|
|
915
|
+
paragraph([
|
|
916
|
+
text("Your first appointment runs about "),
|
|
917
|
+
text("fifty minutes", BOLD),
|
|
918
|
+
text(". We start with a conversation about what brought you in, then a movement assessment, and you leave with a written plan and the first two exercises.")
|
|
919
|
+
]),
|
|
920
|
+
paragraph([
|
|
921
|
+
text("Bring comfortable clothes and any recent imaging. If you have questions before you arrive, "),
|
|
922
|
+
{
|
|
923
|
+
type: "link",
|
|
924
|
+
fields: {
|
|
925
|
+
url: "https://example.com/contact",
|
|
926
|
+
newTab: true,
|
|
927
|
+
linkType: "custom"
|
|
928
|
+
},
|
|
929
|
+
children: [text("get in touch")],
|
|
930
|
+
...block,
|
|
931
|
+
version: 3
|
|
932
|
+
},
|
|
933
|
+
text(".")
|
|
934
|
+
]),
|
|
935
|
+
{
|
|
936
|
+
type: "list",
|
|
937
|
+
listType: "bullet",
|
|
938
|
+
tag: "ul",
|
|
939
|
+
start: 1,
|
|
940
|
+
children: [
|
|
941
|
+
"Assessment and written plan",
|
|
942
|
+
"Hands-on treatment where it helps",
|
|
943
|
+
"Exercises you can do at home, with video"
|
|
944
|
+
].map((item, i) => ({
|
|
945
|
+
type: "listitem",
|
|
946
|
+
value: i + 1,
|
|
947
|
+
children: [text(item)],
|
|
948
|
+
...block
|
|
949
|
+
})),
|
|
950
|
+
...block
|
|
951
|
+
}
|
|
952
|
+
]
|
|
953
|
+
} }
|
|
954
|
+
};
|
|
955
|
+
//#endregion
|
|
956
|
+
//#region src/blocks/showcase-panels/sample.ts
|
|
957
|
+
/** Three panels: the minimum the layout is designed around. */
|
|
958
|
+
const showcasePanelsSample = {
|
|
959
|
+
id: "sample-showcase-panels",
|
|
960
|
+
blockType: "showcasePanels",
|
|
961
|
+
items: [
|
|
962
|
+
{
|
|
963
|
+
id: "sample-panel-back",
|
|
964
|
+
title: "Back and neck pain",
|
|
965
|
+
summary: "Most back pain settles with the right movement, not rest. We find what is driving yours and build a plan around your week, not ours.",
|
|
966
|
+
image: sampleImage({
|
|
967
|
+
label: "Panel 1 · 1400 × 1000",
|
|
968
|
+
width: 1400,
|
|
969
|
+
height: 1e3,
|
|
970
|
+
alt: "A physiotherapist guiding a patient through a stretch"
|
|
971
|
+
}),
|
|
972
|
+
href: "/services/back-and-neck",
|
|
973
|
+
numeral: "01"
|
|
974
|
+
},
|
|
975
|
+
{
|
|
976
|
+
id: "sample-panel-sport",
|
|
977
|
+
title: "Sports injuries",
|
|
978
|
+
summary: "From a rolled ankle to a post-surgical knee: a return-to-play plan with clear milestones, so you know when you are ready.",
|
|
979
|
+
image: sampleImage({
|
|
980
|
+
label: "Panel 2 · 1400 × 1000",
|
|
981
|
+
width: 1400,
|
|
982
|
+
height: 1e3,
|
|
983
|
+
alt: "A runner mid-stride on a track"
|
|
984
|
+
}),
|
|
985
|
+
href: "/services/sports-injuries"
|
|
986
|
+
},
|
|
987
|
+
{
|
|
988
|
+
id: "sample-panel-post-op",
|
|
989
|
+
title: "Post-operative rehab",
|
|
990
|
+
summary: "We work from your surgeon's protocol and keep them in the loop, so every stage of recovery is signed off before the next begins.",
|
|
991
|
+
image: sampleImage({
|
|
992
|
+
label: "Panel 3 · 1400 × 1000",
|
|
993
|
+
width: 1400,
|
|
994
|
+
height: 1e3,
|
|
995
|
+
alt: "A patient on a rehabilitation bike"
|
|
996
|
+
})
|
|
997
|
+
}
|
|
998
|
+
],
|
|
999
|
+
spineVariant: "numbered",
|
|
1000
|
+
watermark: "LARKSPUR",
|
|
1001
|
+
defaultActiveIndex: 0
|
|
1002
|
+
};
|
|
1003
|
+
//#endregion
|
|
1004
|
+
//#region src/blocks/testimonial-masonry/sample.ts
|
|
1005
|
+
function avatar(name) {
|
|
1006
|
+
return sampleImage({
|
|
1007
|
+
label: name.split(" ").map((part) => part[0]).join(""),
|
|
1008
|
+
width: 160,
|
|
1009
|
+
height: 160,
|
|
1010
|
+
alt: `Portrait of ${name}`
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
//#endregion
|
|
1014
|
+
//#region src/samples.ts
|
|
1015
|
+
/**
|
|
1016
|
+
* Every block the package ships, rendered without a CMS row.
|
|
1017
|
+
*
|
|
1018
|
+
* A Block library page (BIS-52) renders these live so an admin can see each
|
|
1019
|
+
* block before enabling it. Each sample lives beside its block's `config.ts`
|
|
1020
|
+
* and `component.tsx` as `sample.ts`, and `src/__tests__/samples.test.tsx`
|
|
1021
|
+
* locks the contract: one entry per slug, `blockType` matching the key, every
|
|
1022
|
+
* config field exercised at least once, and every image self-contained.
|
|
1023
|
+
*
|
|
1024
|
+
* React-free on purpose: it is read from a Global's field config, which
|
|
1025
|
+
* Payload loads in plain Node like the rest of this entry, as well as from a
|
|
1026
|
+
* route handler. Only what this package ships is here; a site supplies samples
|
|
1027
|
+
* for its own blocks through the Block library factory, whose option BIS-52
|
|
1028
|
+
* names.
|
|
1029
|
+
*/
|
|
1030
|
+
const blockSamples = {
|
|
1031
|
+
hero: heroSample,
|
|
1032
|
+
richText: richTextSample,
|
|
1033
|
+
showcasePanels: showcasePanelsSample,
|
|
1034
|
+
processSteps: processStepsSample,
|
|
1035
|
+
faqColumns: faqColumnsSample,
|
|
1036
|
+
testimonialMasonry: {
|
|
1037
|
+
id: "sample-testimonial-masonry",
|
|
1038
|
+
blockType: "testimonialMasonry",
|
|
1039
|
+
eyebrow: "From our patients",
|
|
1040
|
+
title: "What people say after they finish",
|
|
1041
|
+
description: "Every review here was left by a patient we discharged. We do not edit them.",
|
|
1042
|
+
items: [
|
|
1043
|
+
{
|
|
1044
|
+
id: "sample-quote-1",
|
|
1045
|
+
content: "I had written off running. Eight weeks later I did a parkrun, and the plan I was given actually fitted around a job and two kids.",
|
|
1046
|
+
author: {
|
|
1047
|
+
name: "Priya Natarajan",
|
|
1048
|
+
title: "Recovered from a hamstring tear",
|
|
1049
|
+
avatar: avatar("Priya Natarajan")
|
|
1050
|
+
}
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
id: "sample-quote-2",
|
|
1054
|
+
content: "The first physio who explained what was wrong in words I understood.",
|
|
1055
|
+
author: {
|
|
1056
|
+
name: "Tom Ferreira",
|
|
1057
|
+
title: "Lower back pain",
|
|
1058
|
+
avatar: avatar("Tom Ferreira")
|
|
1059
|
+
}
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
id: "sample-quote-3",
|
|
1063
|
+
content: "My surgeon said my knee was ahead of schedule at every check-in. That was the rehab, not me.",
|
|
1064
|
+
author: {
|
|
1065
|
+
name: "Aisha Bello",
|
|
1066
|
+
title: "ACL reconstruction"
|
|
1067
|
+
}
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
id: "sample-quote-4",
|
|
1071
|
+
content: "Booked on a Tuesday, seen on the Thursday. The shoulder I had put up with for a year was sorted in six visits.",
|
|
1072
|
+
author: {
|
|
1073
|
+
name: "Marcus Whitfield",
|
|
1074
|
+
title: "Frozen shoulder",
|
|
1075
|
+
avatar: avatar("Marcus Whitfield")
|
|
1076
|
+
}
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
id: "sample-quote-5",
|
|
1080
|
+
content: "They kept my consultant in the loop the whole way through, which nobody else had bothered to do.",
|
|
1081
|
+
author: { name: "Helen Ostrowski" }
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
id: "sample-quote-6",
|
|
1085
|
+
content: "Honest about what would take time and what would not. I would send my parents here.",
|
|
1086
|
+
author: {
|
|
1087
|
+
name: "Daniel Kim",
|
|
1088
|
+
title: "Ankle sprain",
|
|
1089
|
+
avatar: avatar("Daniel Kim")
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
],
|
|
1093
|
+
link: {
|
|
1094
|
+
label: "Read every review",
|
|
1095
|
+
href: "https://example.com/reviews",
|
|
1096
|
+
newTab: true
|
|
1097
|
+
},
|
|
1098
|
+
minItemsForFade: 6,
|
|
1099
|
+
maxVisibleRows: 2
|
|
1100
|
+
},
|
|
1101
|
+
nap: napSample
|
|
1102
|
+
};
|
|
1103
|
+
//#endregion
|
|
1104
|
+
export { FaqColumnsBlock, HeroBlock, MIN_ROWS_ARRAY_FIELD, NapBlock, ProcessStepsBlock, RichTextBlock, ShowcasePanelsBlock, TestimonialMasonryBlock, blockSamples, emptyRows, headingFields, imageField, linkField, linkFields, resolveMedia, sampleImage };
|
|
673
1105
|
|
|
674
1106
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/fields/image.ts","../src/fields/link.ts","../src/blocks/hero/config.ts","../src/blocks/rich-text/config.ts","../src/fields/min-rows.ts","../src/blocks/showcase-panels/config.ts","../src/blocks/process-steps/config.ts","../src/fields/heading.ts","../src/blocks/faq-columns/config.ts","../src/blocks/testimonial-masonry/config.ts","../src/blocks/nap/config.ts","../src/media.ts"],"sourcesContent":["import type { CollectionSlug, UploadField } from \"payload\";\n\n/**\n * Payload's `UploadField` is a union over the polymorphic (`relationTo: [...]`)\n * and `hasMany` variants. A block image is neither, and narrowing here is what\n * lets `imageField({ name: 'portrait' })` stay type-checked at the call site.\n */\ntype MediaUploadField = Extract<\n UploadField,\n { hasMany?: false; relationTo: CollectionSlug }\n>;\n\nexport interface ImageFieldOptions extends Partial<MediaUploadField> {\n /**\n * The upload collection to point at. Every Bison Lab site names it `media`,\n * but a site that does not can say so without forking the field.\n */\n relationTo?: CollectionSlug;\n}\n\n/**\n * A single image from the site's upload collection, for any block with a\n * picture in it.\n *\n * Optional unless the block says otherwise: `imageField({ name: 'portrait',\n * required: true })`. `admin` is replaced, not merged, so a block that passes\n * its own description writes the whole thing.\n */\nexport function imageField(overrides: ImageFieldOptions = {}): MediaUploadField {\n return {\n name: \"image\",\n type: \"upload\",\n relationTo: \"media\" as CollectionSlug,\n // Payload stars a required field and says nothing about an optional one,\n // so an optional image says so itself, and only then.\n ...(overrides.required ? {} : { admin: { description: \"Optional.\" } }),\n ...overrides,\n } as MediaUploadField;\n}\n","import type { Field, GroupField } from \"payload\";\n\nexport interface LinkFieldsOptions {\n /** Require the label and destination. Defaults to `false`. */\n required?: boolean;\n /** Wording for the visible text field. Defaults to \"Label\". */\n labelFieldLabel?: string;\n}\n\n/**\n * The three fields a call to action is made of, unwrapped.\n *\n * `href` is plain text rather than a relationship to a `pages` row: a block in\n * this package has to work on a site whose page collection is named something\n * else, or which links out more often than in. Resolving an internal reference\n * is the site's job.\n *\n * `newTab` drives `target=\"_blank\"` *and* the matching `rel`, which is why no\n * renderer takes one without the other.\n */\nexport function linkFields({\n required = false,\n labelFieldLabel = \"Label\",\n}: LinkFieldsOptions = {}): Field[] {\n return [\n { name: \"label\", type: \"text\", label: labelFieldLabel, required },\n {\n name: \"href\",\n type: \"text\",\n required,\n admin: {\n description:\n 'A site path (\"/contact\") or a full URL (\"https://example.com\").',\n },\n },\n {\n name: \"newTab\",\n type: \"checkbox\",\n label: \"Open in a new tab\",\n defaultValue: false,\n },\n ];\n}\n\nexport interface LinkFieldOptions extends LinkFieldsOptions {\n /** Field name. Defaults to `link`. */\n name?: string;\n label?: GroupField[\"label\"];\n admin?: GroupField[\"admin\"];\n}\n\n/** `linkFields()` as one named group, for a block with a single CTA. */\nexport function linkField({\n name = \"link\",\n label,\n admin,\n ...rest\n}: LinkFieldOptions = {}): GroupField {\n return {\n name,\n type: \"group\",\n fields: linkFields(rest),\n ...(label === undefined ? {} : { label }),\n ...(admin === undefined ? {} : { admin }),\n };\n}\n\n/** The shape `linkField`/`linkFields` produce once Payload generates types. */\nexport interface LinkValue {\n label?: string | null;\n href?: string | null;\n newTab?: boolean | null;\n}\n","import type { Block } from \"payload\";\n\nimport { imageField } from \"../../fields/image\";\nimport { linkFields } from \"../../fields/link\";\n\n/**\n * The band a page opens with.\n *\n * Unlike every other block here this one has no `@bison-lab/ui` counterpart:\n * a hero is where a site's brand is loudest, and the shipped renderer is\n * deliberately plain markup so a site can swap in its own by overriding this\n * one registry entry. The *fields* are the shared part — that is what makes a\n * page portable between sites.\n */\nexport const HeroBlock: Block = {\n slug: \"hero\",\n interfaceName: \"HeroBlock\",\n labels: { singular: \"Hero\", plural: \"Heroes\" },\n fields: [\n {\n name: \"eyebrow\",\n type: \"text\",\n admin: { description: \"Small label above the heading. Optional.\" },\n },\n { name: \"heading\", type: \"text\", required: true },\n { name: \"body\", type: \"textarea\" },\n {\n name: \"tone\",\n type: \"select\",\n defaultValue: \"sweep\",\n options: [\n { label: \"Sweep (brand gradient)\", value: \"sweep\" },\n { label: \"Dark (flat brand band)\", value: \"dark\" },\n // Describes the surface, not a lightness: in the dark theme the page\n // surface is dark. The value stays `light` so no site migrates.\n { label: \"Plain (page surface)\", value: \"light\" },\n ],\n admin: {\n description:\n \"How the band is painted. Sites map these to their own surfaces.\",\n },\n },\n imageField({ admin: { description: \"Optional background or lead image.\" } }),\n {\n name: \"links\",\n type: \"array\",\n // Two is the point at which a hero stops having a primary action.\n maxRows: 2,\n labels: { singular: \"Call to action\", plural: \"Calls to action\" },\n fields: linkFields({ required: true }),\n },\n ],\n};\n","import type { Block } from \"payload\";\n\n/** A prose section: one Lexical document at reading measure. */\nexport const RichTextBlock: Block = {\n slug: \"richText\",\n interfaceName: \"RichTextBlock\",\n labels: { singular: \"Rich Text\", plural: \"Rich Text\" },\n fields: [{ name: \"content\", type: \"richText\", required: true }],\n};\n","/**\n * The import-map path of the array field that refuses to go below `minRows`.\n *\n * Payload gates *Add* on `maxRows` but never gates *Remove* on `minRows`: an\n * editor can delete the third showcase panel, see a \"requires 3 panels\"\n * note, and only learn on publish that the section is invalid. Payload has no\n * option for this, so it is an admin component (`@bison-lab/payload-blocks/admin`)\n * referenced here by package specifier. Every array in this package with a\n * `minRows` uses it, and a site can put it on its own arrays:\n *\n * ```ts\n * { name: 'items', type: 'array', minRows: 2,\n * admin: { components: { Field: MIN_ROWS_ARRAY_FIELD } }, fields: [...] }\n * ```\n *\n * Consuming sites pick it up by re-running `payload generate:importmap`. Until\n * they do, Payload logs the missing entry and falls back to its stock field.\n */\nexport const MIN_ROWS_ARRAY_FIELD =\n \"@bison-lab/payload-blocks/admin#MinRowsArrayField\";\n\n/** Empty rows for an array's `defaultValue`, so a block opens at its minimum. */\nexport function emptyRows(count: number): Record<string, never>[] {\n return Array.from({ length: count }, () => ({}));\n}\n","import type { Block } from \"payload\";\n\nimport { imageField } from \"../../fields/image\";\nimport { emptyRows, MIN_ROWS_ARRAY_FIELD } from \"../../fields/min-rows\";\n\n/** Expanding panels, one per service or product line. */\nexport const ShowcasePanelsBlock: Block = {\n slug: \"showcasePanels\",\n interfaceName: \"ShowcasePanelsBlock\",\n labels: { singular: \"Showcase Panels\", plural: \"Showcase Panels\" },\n fields: [\n {\n name: \"items\",\n type: \"array\",\n required: true,\n // The layout is designed around 3-6 panels: fewer and the accordion\n // reads as a mistake, more and each collapsed spine is unreadable. The\n // block opens with the minimum already in place, and the admin field\n // refuses to go below it.\n minRows: 3,\n maxRows: 6,\n defaultValue: emptyRows(3),\n labels: { singular: \"Panel\", plural: \"Panels\" },\n admin: { components: { Field: MIN_ROWS_ARRAY_FIELD } },\n fields: [\n { name: \"title\", type: \"text\", required: true },\n {\n name: \"summary\",\n type: \"textarea\",\n required: true,\n admin: { description: \"Revealed when the panel expands.\" },\n },\n imageField({ required: true }),\n {\n name: \"href\",\n type: \"text\",\n admin: {\n description: 'Optional \"read more\" destination for this panel.',\n },\n },\n {\n name: \"numeral\",\n type: \"text\",\n admin: {\n description:\n 'Overrides the spine numeral (\"01\", \"II\"). Leave empty to number automatically.',\n },\n },\n ],\n },\n {\n name: \"spineVariant\",\n type: \"select\",\n defaultValue: \"numbered\",\n options: [\n { label: \"Numbered (01, 02)\", value: \"numbered\" },\n { label: \"Volume (I, II)\", value: \"volume\" },\n { label: \"Minimal (no numeral)\", value: \"minimal\" },\n ],\n },\n {\n name: \"watermark\",\n type: \"text\",\n admin: { description: 'Faint mark behind the panels, e.g. a brand word.' },\n },\n {\n name: \"defaultActiveIndex\",\n type: \"number\",\n defaultValue: 0,\n min: 0,\n admin: { description: \"Which panel is open on load, counting from 0.\" },\n },\n // The region's accessible name and the selected/preview state text are\n // not editor concerns; the `@bison-lab/ui` defaults stand.\n ],\n};\n","import type { Block } from \"payload\";\n\nimport { imageField } from \"../../fields/image\";\nimport { emptyRows, MIN_ROWS_ARRAY_FIELD } from \"../../fields/min-rows\";\n\n/** An ordered walkthrough that advances on its own. */\nexport const ProcessStepsBlock: Block = {\n slug: \"processSteps\",\n interfaceName: \"ProcessStepsBlock\",\n labels: { singular: \"Process Steps\", plural: \"Process Steps\" },\n fields: [\n {\n name: \"items\",\n type: \"array\",\n required: true,\n // Same reasoning as the showcase panels: the step rail is designed\n // around 3-6 entries, in order. The block opens with the minimum\n // already in place, and the admin field refuses to go below it.\n minRows: 3,\n maxRows: 6,\n defaultValue: emptyRows(3),\n labels: { singular: \"Step\", plural: \"Steps\" },\n admin: { components: { Field: MIN_ROWS_ARRAY_FIELD } },\n // Steps are numbered by position; ordering is drag and drop. There is\n // deliberately no per-step number override.\n fields: [\n { name: \"title\", type: \"text\", required: true },\n { name: \"description\", type: \"textarea\", required: true },\n imageField({ required: true }),\n ],\n },\n {\n name: \"autoAdvance\",\n type: \"checkbox\",\n defaultValue: true,\n admin: {\n description:\n \"Advance on a timer. Always off for visitors who prefer reduced motion.\",\n },\n },\n {\n name: \"autoAdvanceDuration\",\n type: \"number\",\n defaultValue: 6000,\n min: 1000,\n admin: { description: \"Milliseconds each step holds before advancing.\" },\n },\n { name: \"pauseOnHover\", type: \"checkbox\", defaultValue: true },\n {\n name: \"defaultActiveIndex\",\n type: \"number\",\n defaultValue: 0,\n min: 0,\n admin: { description: \"Which step is active on load, counting from 0.\" },\n },\n // The region's accessible name and the play/pause control text are not\n // editor concerns; the `@bison-lab/ui` defaults stand.\n ],\n};\n","import type { Field } from \"payload\";\n\nexport interface HeadingFieldsOptions {\n /** Require the section heading. Defaults to `true`. */\n required?: boolean;\n /** Description shown under the eyebrow input. */\n eyebrowDescription?: string;\n}\n\n/**\n * The eyebrow / title / description trio every marketing band opens with.\n *\n * They are three top-level fields rather than a group: editors read a section\n * heading as the first thing in the block, and burying it one collapse deep\n * puts the least-optional copy behind a click. The field names match the\n * `@bison-lab/ui` prop names so the renderers stay a pass-through.\n */\nexport function headingFields({\n required = true,\n eyebrowDescription = \"Small label above the heading. Leave empty to hide the row.\",\n}: HeadingFieldsOptions = {}): Field[] {\n return [\n {\n name: \"eyebrow\",\n type: \"text\",\n admin: { description: eyebrowDescription },\n },\n { name: \"title\", type: \"text\", required },\n { name: \"description\", type: \"textarea\" },\n ];\n}\n","import type { Block } from \"payload\";\n\nimport { headingFields } from \"../../fields/heading\";\nimport { emptyRows, MIN_ROWS_ARRAY_FIELD } from \"../../fields/min-rows\";\n\n/** A sticky intro beside a column of questions. */\nexport const FaqColumnsBlock: Block = {\n slug: \"faqColumns\",\n interfaceName: \"FaqColumnsBlock\",\n labels: { singular: \"FAQ Columns\", plural: \"FAQ Columns\" },\n fields: [\n ...headingFields(),\n {\n name: \"items\",\n type: \"array\",\n required: true,\n minRows: 1,\n defaultValue: emptyRows(1),\n labels: { singular: \"Question\", plural: \"Questions\" },\n admin: { components: { Field: MIN_ROWS_ARRAY_FIELD } },\n fields: [\n { name: \"question\", type: \"text\", required: true },\n {\n name: \"answer\",\n // Plain text on purpose. A Lexical answer would pull\n // `@payloadcms/richtext-lexical` into the `./react` entry, which\n // every consumer loads; the `richText` block is where prose with\n // links and lists belongs.\n type: \"textarea\",\n required: true,\n },\n ],\n },\n {\n name: \"cta\",\n type: \"group\",\n label: \"Call to action\",\n admin: {\n description: \"Shown under the intro. Leave the link empty to hide it.\",\n },\n fields: [\n {\n name: \"title\",\n type: \"text\",\n admin: { description: 'Lead-in line, e.g. \"Still have questions?\".' },\n },\n { name: \"linkText\", type: \"text\" },\n { name: \"href\", type: \"text\" },\n {\n name: \"newTab\",\n type: \"checkbox\",\n label: \"Open in a new tab\",\n defaultValue: false,\n },\n ],\n },\n {\n name: \"sticky\",\n type: \"checkbox\",\n defaultValue: true,\n admin: { description: \"Pin the intro column while the answers scroll.\" },\n },\n {\n name: \"type\",\n type: \"select\",\n defaultValue: \"single\",\n options: [\n { label: \"One answer open at a time\", value: \"single\" },\n { label: \"Several answers open at once\", value: \"multiple\" },\n ],\n },\n {\n name: \"collapsible\",\n type: \"checkbox\",\n defaultValue: true,\n admin: {\n description: \"Allow the open answer to be closed again. Single mode only.\",\n },\n },\n ],\n};\n","import type { Block } from \"payload\";\n\nimport { headingFields } from \"../../fields/heading\";\nimport { imageField } from \"../../fields/image\";\nimport { linkField } from \"../../fields/link\";\nimport { emptyRows, MIN_ROWS_ARRAY_FIELD } from \"../../fields/min-rows\";\n\n/** Quotes in a masonry grid, clipped behind a fade once there are enough. */\nexport const TestimonialMasonryBlock: Block = {\n slug: \"testimonialMasonry\",\n interfaceName: \"TestimonialMasonryBlock\",\n labels: { singular: \"Testimonial Masonry\", plural: \"Testimonial Masonry\" },\n fields: [\n ...headingFields(),\n {\n name: \"items\",\n type: \"array\",\n required: true,\n minRows: 1,\n defaultValue: emptyRows(1),\n labels: { singular: \"Testimonial\", plural: \"Testimonials\" },\n admin: { components: { Field: MIN_ROWS_ARRAY_FIELD } },\n fields: [\n { name: \"content\", type: \"textarea\", required: true, label: \"Quote\" },\n {\n name: \"author\",\n type: \"group\",\n fields: [\n { name: \"name\", type: \"text\", required: true },\n {\n name: \"title\",\n type: \"text\",\n admin: {\n description: 'Role or organisation, e.g. \"Orthopaedic surgeon\".',\n },\n },\n imageField({\n name: \"avatar\",\n admin: {\n description:\n \"Optional. Initials from the name are used when empty.\",\n },\n }),\n ],\n },\n ],\n },\n linkField({\n label: \"Link\",\n admin: {\n description:\n \"Shown under the fade, once there are enough quotes to clip.\",\n },\n }),\n {\n name: \"minItemsForFade\",\n type: \"number\",\n defaultValue: 7,\n min: 1,\n admin: {\n description:\n \"How many quotes before the grid clips and the bottom fade appears.\",\n },\n },\n {\n name: \"maxVisibleRows\",\n type: \"number\",\n min: 1,\n admin: { description: \"Rows shown before the fade. Optional.\" },\n },\n ],\n};\n","import type { Block } from \"payload\";\n\nimport { emptyRows, MIN_ROWS_ARRAY_FIELD } from \"../../fields/min-rows\";\n\n/**\n * Name, address, phone — the block local SEO is graded on.\n *\n * The `e164` phone number is a separate field from the displayed one because\n * `NapBlock` uses it, and only it, for the `tel:` href and the JSON-LD\n * `telephone`. A prettified string can never silently become an unreachable\n * link.\n */\nexport const NapBlock: Block = {\n slug: \"nap\",\n interfaceName: \"NapBlock\",\n labels: { singular: \"Name, Address, Phone\", plural: \"Name, Address, Phone\" },\n fields: [\n { name: \"businessName\", type: \"text\", required: true, label: \"Business name\" },\n {\n name: \"businessType\",\n type: \"text\",\n required: true,\n defaultValue: \"LocalBusiness\",\n admin: {\n description:\n 'schema.org type, e.g. \"MedicalBusiness\", \"Chiropractor\", \"LocalBusiness\".',\n },\n },\n {\n name: \"address\",\n type: \"group\",\n fields: [\n { name: \"streetAddress\", type: \"text\", required: true },\n { name: \"addressLocality\", type: \"text\", required: true, label: \"City\" },\n {\n name: \"addressRegion\",\n type: \"text\",\n required: true,\n label: \"State or region\",\n },\n { name: \"postalCode\", type: \"text\", required: true },\n {\n name: \"addressCountry\",\n type: \"text\",\n defaultValue: \"US\",\n admin: { description: \"ISO 3166-1 alpha-2, e.g. US.\" },\n },\n ],\n },\n {\n name: \"departments\",\n type: \"array\",\n required: true,\n minRows: 1,\n defaultValue: emptyRows(1),\n labels: { singular: \"Department\", plural: \"Departments\" },\n admin: {\n components: { Field: MIN_ROWS_ARRAY_FIELD },\n description:\n \"A single-location business has one entry, usually named after the business.\",\n },\n fields: [\n { name: \"name\", type: \"text\", required: true },\n {\n name: \"departmentType\",\n type: \"text\",\n admin: {\n description:\n \"schema.org type for this department. Falls back to the business type.\",\n },\n },\n {\n name: \"phoneE164\",\n type: \"text\",\n required: true,\n label: \"Phone (E.164)\",\n admin: {\n description:\n 'Dialable form, e.g. \"+15551234567\". This is what the tel: link and the structured data use.',\n },\n },\n {\n name: \"phoneDisplay\",\n type: \"text\",\n label: \"Phone (as displayed)\",\n admin: {\n description:\n \"Optional. US numbers are formatted automatically when this is empty.\",\n },\n },\n ],\n },\n {\n name: \"url\",\n type: \"text\",\n admin: { description: \"Canonical URL for the business. Optional.\" },\n },\n {\n name: \"showName\",\n type: \"checkbox\",\n defaultValue: true,\n admin: {\n description:\n \"Turn off when a heading above the block already names the business.\",\n },\n },\n {\n name: \"headingLevel\",\n type: \"select\",\n defaultValue: \"h2\",\n options: [\n { label: \"H2\", value: \"h2\" },\n { label: \"H3\", value: \"h3\" },\n { label: \"H4\", value: \"h4\" },\n ],\n admin: { description: \"Keeps the page's heading order intact.\" },\n },\n {\n name: \"emitJsonLd\",\n type: \"checkbox\",\n defaultValue: true,\n label: \"Emit structured data\",\n admin: {\n description:\n \"Adds a schema.org LocalBusiness script. Turn off if the page emits its own.\",\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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/fields/image.ts","../src/fields/link.ts","../src/blocks/hero/config.ts","../src/blocks/rich-text/config.ts","../src/fields/min-rows.ts","../src/blocks/showcase-panels/config.ts","../src/blocks/process-steps/config.ts","../src/fields/heading.ts","../src/blocks/faq-columns/config.ts","../src/blocks/testimonial-masonry/config.ts","../src/blocks/nap/config.ts","../src/media.ts","../src/blocks/faq-columns/sample.ts","../src/sample-image.ts","../src/blocks/hero/sample.ts","../src/blocks/nap/sample.ts","../src/blocks/process-steps/sample.ts","../src/blocks/rich-text/sample.ts","../src/blocks/showcase-panels/sample.ts","../src/blocks/testimonial-masonry/sample.ts","../src/samples.ts"],"sourcesContent":["import type { CollectionSlug, UploadField } from \"payload\";\n\n/**\n * Payload's `UploadField` is a union over the polymorphic (`relationTo: [...]`)\n * and `hasMany` variants. A block image is neither, and narrowing here is what\n * lets `imageField({ name: 'portrait' })` stay type-checked at the call site.\n */\ntype MediaUploadField = Extract<\n UploadField,\n { hasMany?: false; relationTo: CollectionSlug }\n>;\n\nexport interface ImageFieldOptions extends Partial<MediaUploadField> {\n /**\n * The upload collection to point at. Every Bison Lab site names it `media`,\n * but a site that does not can say so without forking the field.\n */\n relationTo?: CollectionSlug;\n}\n\n/**\n * A single image from the site's upload collection, for any block with a\n * picture in it.\n *\n * Optional unless the block says otherwise: `imageField({ name: 'portrait',\n * required: true })`. `admin` is replaced, not merged, so a block that passes\n * its own description writes the whole thing.\n */\nexport function imageField(overrides: ImageFieldOptions = {}): MediaUploadField {\n return {\n name: \"image\",\n type: \"upload\",\n relationTo: \"media\" as CollectionSlug,\n // Payload stars a required field and says nothing about an optional one,\n // so an optional image says so itself, and only then.\n ...(overrides.required ? {} : { admin: { description: \"Optional.\" } }),\n ...overrides,\n } as MediaUploadField;\n}\n","import type { Field, GroupField } from \"payload\";\n\nexport interface LinkFieldsOptions {\n /** Require the label and destination. Defaults to `false`. */\n required?: boolean;\n /** Wording for the visible text field. Defaults to \"Label\". */\n labelFieldLabel?: string;\n}\n\n/**\n * The three fields a call to action is made of, unwrapped.\n *\n * `href` is plain text rather than a relationship to a `pages` row: a block in\n * this package has to work on a site whose page collection is named something\n * else, or which links out more often than in. Resolving an internal reference\n * is the site's job.\n *\n * `newTab` drives `target=\"_blank\"` *and* the matching `rel`, which is why no\n * renderer takes one without the other.\n */\nexport function linkFields({\n required = false,\n labelFieldLabel = \"Label\",\n}: LinkFieldsOptions = {}): Field[] {\n return [\n { name: \"label\", type: \"text\", label: labelFieldLabel, required },\n {\n name: \"href\",\n type: \"text\",\n required,\n admin: {\n description:\n 'A site path (\"/contact\") or a full URL (\"https://example.com\").',\n },\n },\n {\n name: \"newTab\",\n type: \"checkbox\",\n label: \"Open in a new tab\",\n defaultValue: false,\n },\n ];\n}\n\nexport interface LinkFieldOptions extends LinkFieldsOptions {\n /** Field name. Defaults to `link`. */\n name?: string;\n label?: GroupField[\"label\"];\n admin?: GroupField[\"admin\"];\n}\n\n/** `linkFields()` as one named group, for a block with a single CTA. */\nexport function linkField({\n name = \"link\",\n label,\n admin,\n ...rest\n}: LinkFieldOptions = {}): GroupField {\n return {\n name,\n type: \"group\",\n fields: linkFields(rest),\n ...(label === undefined ? {} : { label }),\n ...(admin === undefined ? {} : { admin }),\n };\n}\n\n/** The shape `linkField`/`linkFields` produce once Payload generates types. */\nexport interface LinkValue {\n label?: string | null;\n href?: string | null;\n newTab?: boolean | null;\n}\n","import type { Block } from \"payload\";\n\nimport { imageField } from \"../../fields/image\";\nimport { linkFields } from \"../../fields/link\";\n\n/**\n * The band a page opens with.\n *\n * Unlike every other block here this one has no `@bison-lab/ui` counterpart:\n * a hero is where a site's brand is loudest, and the shipped renderer is\n * deliberately plain markup so a site can swap in its own by overriding this\n * one registry entry. The *fields* are the shared part — that is what makes a\n * page portable between sites.\n */\nexport const HeroBlock: Block = {\n slug: \"hero\",\n interfaceName: \"HeroBlock\",\n labels: { singular: \"Hero\", plural: \"Heroes\" },\n fields: [\n {\n name: \"eyebrow\",\n type: \"text\",\n admin: { description: \"Small label above the heading. Optional.\" },\n },\n { name: \"heading\", type: \"text\", required: true },\n { name: \"body\", type: \"textarea\" },\n {\n name: \"tone\",\n type: \"select\",\n defaultValue: \"sweep\",\n options: [\n { label: \"Sweep (brand gradient)\", value: \"sweep\" },\n { label: \"Dark (flat brand band)\", value: \"dark\" },\n // Describes the surface, not a lightness: in the dark theme the page\n // surface is dark. The value stays `light` so no site migrates.\n { label: \"Plain (page surface)\", value: \"light\" },\n ],\n admin: {\n description:\n \"How the band is painted. Sites map these to their own surfaces.\",\n },\n },\n imageField({ admin: { description: \"Optional background or lead image.\" } }),\n {\n name: \"links\",\n type: \"array\",\n // Two is the point at which a hero stops having a primary action.\n maxRows: 2,\n labels: { singular: \"Call to action\", plural: \"Calls to action\" },\n fields: linkFields({ required: true }),\n },\n ],\n};\n","import type { Block } from \"payload\";\n\n/** A prose section: one Lexical document at reading measure. */\nexport const RichTextBlock: Block = {\n slug: \"richText\",\n interfaceName: \"RichTextBlock\",\n labels: { singular: \"Rich Text\", plural: \"Rich Text\" },\n fields: [{ name: \"content\", type: \"richText\", required: true }],\n};\n","/**\n * The import-map path of the array field that refuses to go below `minRows`.\n *\n * Payload gates *Add* on `maxRows` but never gates *Remove* on `minRows`: an\n * editor can delete the third showcase panel, see a \"requires 3 panels\"\n * note, and only learn on publish that the section is invalid. Payload has no\n * option for this, so it is an admin component (`@bison-lab/payload-blocks/admin`)\n * referenced here by package specifier. Every array in this package with a\n * `minRows` uses it, and a site can put it on its own arrays:\n *\n * ```ts\n * { name: 'items', type: 'array', minRows: 2,\n * admin: { components: { Field: MIN_ROWS_ARRAY_FIELD } }, fields: [...] }\n * ```\n *\n * Consuming sites pick it up by re-running `payload generate:importmap`. Until\n * they do, Payload logs the missing entry and falls back to its stock field.\n */\nexport const MIN_ROWS_ARRAY_FIELD =\n \"@bison-lab/payload-blocks/admin#MinRowsArrayField\";\n\n/** Empty rows for an array's `defaultValue`, so a block opens at its minimum. */\nexport function emptyRows(count: number): Record<string, never>[] {\n return Array.from({ length: count }, () => ({}));\n}\n","import type { Block } from \"payload\";\n\nimport { imageField } from \"../../fields/image\";\nimport { emptyRows, MIN_ROWS_ARRAY_FIELD } from \"../../fields/min-rows\";\n\n/** Expanding panels, one per service or product line. */\nexport const ShowcasePanelsBlock: Block = {\n slug: \"showcasePanels\",\n interfaceName: \"ShowcasePanelsBlock\",\n labels: { singular: \"Showcase Panels\", plural: \"Showcase Panels\" },\n fields: [\n {\n name: \"items\",\n type: \"array\",\n required: true,\n // The layout is designed around 3-6 panels: fewer and the accordion\n // reads as a mistake, more and each collapsed spine is unreadable. The\n // block opens with the minimum already in place, and the admin field\n // refuses to go below it.\n minRows: 3,\n maxRows: 6,\n defaultValue: emptyRows(3),\n labels: { singular: \"Panel\", plural: \"Panels\" },\n admin: { components: { Field: MIN_ROWS_ARRAY_FIELD } },\n fields: [\n { name: \"title\", type: \"text\", required: true },\n {\n name: \"summary\",\n type: \"textarea\",\n required: true,\n admin: { description: \"Revealed when the panel expands.\" },\n },\n imageField({ required: true }),\n {\n name: \"href\",\n type: \"text\",\n admin: {\n description: 'Optional \"read more\" destination for this panel.',\n },\n },\n {\n name: \"numeral\",\n type: \"text\",\n admin: {\n description:\n 'Overrides the spine numeral (\"01\", \"II\"). Leave empty to number automatically.',\n },\n },\n ],\n },\n {\n name: \"spineVariant\",\n type: \"select\",\n defaultValue: \"numbered\",\n options: [\n { label: \"Numbered (01, 02)\", value: \"numbered\" },\n { label: \"Volume (I, II)\", value: \"volume\" },\n { label: \"Minimal (no numeral)\", value: \"minimal\" },\n ],\n },\n {\n name: \"watermark\",\n type: \"text\",\n admin: { description: 'Faint mark behind the panels, e.g. a brand word.' },\n },\n {\n name: \"defaultActiveIndex\",\n type: \"number\",\n defaultValue: 0,\n min: 0,\n admin: { description: \"Which panel is open on load, counting from 0.\" },\n },\n // The region's accessible name and the selected/preview state text are\n // not editor concerns; the `@bison-lab/ui` defaults stand.\n ],\n};\n","import type { Block } from \"payload\";\n\nimport { imageField } from \"../../fields/image\";\nimport { emptyRows, MIN_ROWS_ARRAY_FIELD } from \"../../fields/min-rows\";\n\n/** An ordered walkthrough that advances on its own. */\nexport const ProcessStepsBlock: Block = {\n slug: \"processSteps\",\n interfaceName: \"ProcessStepsBlock\",\n labels: { singular: \"Process Steps\", plural: \"Process Steps\" },\n fields: [\n {\n name: \"items\",\n type: \"array\",\n required: true,\n // Same reasoning as the showcase panels: the step rail is designed\n // around 3-6 entries, in order. The block opens with the minimum\n // already in place, and the admin field refuses to go below it.\n minRows: 3,\n maxRows: 6,\n defaultValue: emptyRows(3),\n labels: { singular: \"Step\", plural: \"Steps\" },\n admin: { components: { Field: MIN_ROWS_ARRAY_FIELD } },\n // Steps are numbered by position; ordering is drag and drop. There is\n // deliberately no per-step number override.\n fields: [\n { name: \"title\", type: \"text\", required: true },\n { name: \"description\", type: \"textarea\", required: true },\n imageField({ required: true }),\n ],\n },\n {\n name: \"autoAdvance\",\n type: \"checkbox\",\n defaultValue: true,\n admin: {\n description:\n \"Advance on a timer. Always off for visitors who prefer reduced motion.\",\n },\n },\n {\n name: \"autoAdvanceDuration\",\n type: \"number\",\n defaultValue: 6000,\n min: 1000,\n admin: { description: \"Milliseconds each step holds before advancing.\" },\n },\n { name: \"pauseOnHover\", type: \"checkbox\", defaultValue: true },\n {\n name: \"defaultActiveIndex\",\n type: \"number\",\n defaultValue: 0,\n min: 0,\n admin: { description: \"Which step is active on load, counting from 0.\" },\n },\n // The region's accessible name and the play/pause control text are not\n // editor concerns; the `@bison-lab/ui` defaults stand.\n ],\n};\n","import type { Field } from \"payload\";\n\nexport interface HeadingFieldsOptions {\n /** Require the section heading. Defaults to `true`. */\n required?: boolean;\n /** Description shown under the eyebrow input. */\n eyebrowDescription?: string;\n}\n\n/**\n * The eyebrow / title / description trio every marketing band opens with.\n *\n * They are three top-level fields rather than a group: editors read a section\n * heading as the first thing in the block, and burying it one collapse deep\n * puts the least-optional copy behind a click. The field names match the\n * `@bison-lab/ui` prop names so the renderers stay a pass-through.\n */\nexport function headingFields({\n required = true,\n eyebrowDescription = \"Small label above the heading. Leave empty to hide the row.\",\n}: HeadingFieldsOptions = {}): Field[] {\n return [\n {\n name: \"eyebrow\",\n type: \"text\",\n admin: { description: eyebrowDescription },\n },\n { name: \"title\", type: \"text\", required },\n { name: \"description\", type: \"textarea\" },\n ];\n}\n","import type { Block } from \"payload\";\n\nimport { headingFields } from \"../../fields/heading\";\nimport { emptyRows, MIN_ROWS_ARRAY_FIELD } from \"../../fields/min-rows\";\n\n/** A sticky intro beside a column of questions. */\nexport const FaqColumnsBlock: Block = {\n slug: \"faqColumns\",\n interfaceName: \"FaqColumnsBlock\",\n labels: { singular: \"FAQ Columns\", plural: \"FAQ Columns\" },\n fields: [\n ...headingFields(),\n {\n name: \"items\",\n type: \"array\",\n required: true,\n minRows: 1,\n defaultValue: emptyRows(1),\n labels: { singular: \"Question\", plural: \"Questions\" },\n admin: { components: { Field: MIN_ROWS_ARRAY_FIELD } },\n fields: [\n { name: \"question\", type: \"text\", required: true },\n {\n name: \"answer\",\n // Plain text on purpose. A Lexical answer would pull\n // `@payloadcms/richtext-lexical` into the `./react` entry, which\n // every consumer loads; the `richText` block is where prose with\n // links and lists belongs.\n type: \"textarea\",\n required: true,\n },\n ],\n },\n {\n name: \"cta\",\n type: \"group\",\n label: \"Call to action\",\n admin: {\n description: \"Shown under the intro. Leave the link empty to hide it.\",\n },\n fields: [\n {\n name: \"title\",\n type: \"text\",\n admin: { description: 'Lead-in line, e.g. \"Still have questions?\".' },\n },\n { name: \"linkText\", type: \"text\" },\n { name: \"href\", type: \"text\" },\n {\n name: \"newTab\",\n type: \"checkbox\",\n label: \"Open in a new tab\",\n defaultValue: false,\n },\n ],\n },\n {\n name: \"sticky\",\n type: \"checkbox\",\n defaultValue: true,\n admin: { description: \"Pin the intro column while the answers scroll.\" },\n },\n {\n name: \"type\",\n type: \"select\",\n defaultValue: \"single\",\n options: [\n { label: \"One answer open at a time\", value: \"single\" },\n { label: \"Several answers open at once\", value: \"multiple\" },\n ],\n },\n {\n name: \"collapsible\",\n type: \"checkbox\",\n defaultValue: true,\n admin: {\n description: \"Allow the open answer to be closed again. Single mode only.\",\n },\n },\n ],\n};\n","import type { Block } from \"payload\";\n\nimport { headingFields } from \"../../fields/heading\";\nimport { imageField } from \"../../fields/image\";\nimport { linkField } from \"../../fields/link\";\nimport { emptyRows, MIN_ROWS_ARRAY_FIELD } from \"../../fields/min-rows\";\n\n/** Quotes in a masonry grid, clipped behind a fade once there are enough. */\nexport const TestimonialMasonryBlock: Block = {\n slug: \"testimonialMasonry\",\n interfaceName: \"TestimonialMasonryBlock\",\n labels: { singular: \"Testimonial Masonry\", plural: \"Testimonial Masonry\" },\n fields: [\n ...headingFields(),\n {\n name: \"items\",\n type: \"array\",\n required: true,\n minRows: 1,\n defaultValue: emptyRows(1),\n labels: { singular: \"Testimonial\", plural: \"Testimonials\" },\n admin: { components: { Field: MIN_ROWS_ARRAY_FIELD } },\n fields: [\n { name: \"content\", type: \"textarea\", required: true, label: \"Quote\" },\n {\n name: \"author\",\n type: \"group\",\n fields: [\n { name: \"name\", type: \"text\", required: true },\n {\n name: \"title\",\n type: \"text\",\n admin: {\n description: 'Role or organisation, e.g. \"Orthopaedic surgeon\".',\n },\n },\n imageField({\n name: \"avatar\",\n admin: {\n description:\n \"Optional. Initials from the name are used when empty.\",\n },\n }),\n ],\n },\n ],\n },\n linkField({\n label: \"Link\",\n admin: {\n description:\n \"Shown under the fade, once there are enough quotes to clip.\",\n },\n }),\n {\n name: \"minItemsForFade\",\n type: \"number\",\n defaultValue: 7,\n min: 1,\n admin: {\n description:\n \"How many quotes before the grid clips and the bottom fade appears.\",\n },\n },\n {\n name: \"maxVisibleRows\",\n type: \"number\",\n min: 1,\n admin: { description: \"Rows shown before the fade. Optional.\" },\n },\n ],\n};\n","import type { Block } from \"payload\";\n\nimport { emptyRows, MIN_ROWS_ARRAY_FIELD } from \"../../fields/min-rows\";\n\n/**\n * Name, address, phone — the block local SEO is graded on.\n *\n * The `e164` phone number is a separate field from the displayed one because\n * `NapBlock` uses it, and only it, for the `tel:` href and the JSON-LD\n * `telephone`. A prettified string can never silently become an unreachable\n * link.\n */\nexport const NapBlock: Block = {\n slug: \"nap\",\n interfaceName: \"NapBlock\",\n labels: { singular: \"Name, Address, Phone\", plural: \"Name, Address, Phone\" },\n fields: [\n { name: \"businessName\", type: \"text\", required: true, label: \"Business name\" },\n {\n name: \"businessType\",\n type: \"text\",\n required: true,\n defaultValue: \"LocalBusiness\",\n admin: {\n description:\n 'schema.org type, e.g. \"MedicalBusiness\", \"Chiropractor\", \"LocalBusiness\".',\n },\n },\n {\n name: \"address\",\n type: \"group\",\n fields: [\n { name: \"streetAddress\", type: \"text\", required: true },\n { name: \"addressLocality\", type: \"text\", required: true, label: \"City\" },\n {\n name: \"addressRegion\",\n type: \"text\",\n required: true,\n label: \"State or region\",\n },\n { name: \"postalCode\", type: \"text\", required: true },\n {\n name: \"addressCountry\",\n type: \"text\",\n defaultValue: \"US\",\n admin: { description: \"ISO 3166-1 alpha-2, e.g. US.\" },\n },\n ],\n },\n {\n name: \"departments\",\n type: \"array\",\n required: true,\n minRows: 1,\n defaultValue: emptyRows(1),\n labels: { singular: \"Department\", plural: \"Departments\" },\n admin: {\n components: { Field: MIN_ROWS_ARRAY_FIELD },\n description:\n \"A single-location business has one entry, usually named after the business.\",\n },\n fields: [\n { name: \"name\", type: \"text\", required: true },\n {\n name: \"departmentType\",\n type: \"text\",\n admin: {\n description:\n \"schema.org type for this department. Falls back to the business type.\",\n },\n },\n {\n name: \"phoneE164\",\n type: \"text\",\n required: true,\n label: \"Phone (E.164)\",\n admin: {\n description:\n 'Dialable form, e.g. \"+15551234567\". This is what the tel: link and the structured data use.',\n },\n },\n {\n name: \"phoneDisplay\",\n type: \"text\",\n label: \"Phone (as displayed)\",\n admin: {\n description:\n \"Optional. US numbers are formatted automatically when this is empty.\",\n },\n },\n ],\n },\n {\n name: \"url\",\n type: \"text\",\n admin: { description: \"Canonical URL for the business. Optional.\" },\n },\n {\n name: \"showName\",\n type: \"checkbox\",\n defaultValue: true,\n admin: {\n description:\n \"Turn off when a heading above the block already names the business.\",\n },\n },\n {\n name: \"headingLevel\",\n type: \"select\",\n defaultValue: \"h2\",\n options: [\n { label: \"H2\", value: \"h2\" },\n { label: \"H3\", value: \"h3\" },\n { label: \"H4\", value: \"h4\" },\n ],\n admin: { description: \"Keeps the page's heading order intact.\" },\n },\n {\n name: \"emitJsonLd\",\n type: \"checkbox\",\n defaultValue: true,\n label: \"Emit structured data\",\n admin: {\n description:\n \"Adds a schema.org LocalBusiness script. Turn off if the page emits its own.\",\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 type { FaqColumnsBlockData } from \"../../types\";\n\nexport const faqColumnsSample: FaqColumnsBlockData = {\n id: \"sample-faq-columns\",\n blockType: \"faqColumns\",\n eyebrow: \"Good to know\",\n title: \"Questions patients ask before their first visit\",\n description:\n \"If yours is not here, the front desk answers the phone between eight and six on weekdays.\",\n items: [\n {\n id: \"sample-faq-referral\",\n question: \"Do I need a referral?\",\n answer:\n \"No. You can book directly. Some insurers ask for one before they reimburse, so check your policy if you plan to claim.\",\n },\n {\n id: \"sample-faq-sessions\",\n question: \"How many sessions will I need?\",\n answer:\n \"Most plans finish in six to eight sessions. You will get a written estimate after your assessment, and we revise it with you as you progress.\",\n },\n {\n id: \"sample-faq-wear\",\n question: \"What should I wear?\",\n answer:\n \"Anything you can move in. Shorts for a knee or hip, a vest or loose top for a shoulder or neck.\\n\\nThere are changing rooms if you are coming from work.\",\n },\n {\n id: \"sample-faq-insurance\",\n question: \"Is treatment covered by insurance?\",\n answer:\n \"Usually, once conservative treatment has been recommended. We invoice you directly and give you everything the insurer needs to reimburse you.\",\n },\n {\n id: \"sample-faq-cancel\",\n question: \"What is the cancellation policy?\",\n answer:\n \"Twenty-four hours' notice, by phone or through the booking link in your confirmation email. Later than that and the session is charged.\",\n },\n ],\n cta: {\n title: \"Still have questions?\",\n linkText: \"Call the front desk\",\n href: \"tel:+13035550142\",\n newTab: false,\n },\n sticky: true,\n type: \"single\",\n collapsible: true,\n};\n","import type { MediaDoc } from \"./media\";\n\nexport interface SampleImageOptions {\n /** Text drawn across the placeholder, e.g. \"Panel 1 · 1400 × 1000\". */\n label: string;\n width: number;\n height: number;\n /** Alt text for the document. Defaults to the label. */\n alt?: string;\n}\n\nfunction escapeXml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\");\n}\n\n/**\n * A placeholder upload document for a block sample.\n *\n * A preview has no site media behind it, so the image has to travel with the\n * sample. This is an inline SVG as a `data:` URL, in the `MediaDoc` shape\n * `resolveMedia` already narrows, with `width` and `height` set so an image\n * adapter can reserve the box. The scheme is what tells an adapter which kind\n * of source it has: `next/image` treats a `data:` src as `unoptimized` on its\n * own, so a site's adapter needs no special case for samples.\n *\n * Neutral greys rather than theme tokens: the SVG is a standalone document\n * and cannot see the page's custom properties.\n */\nexport function sampleImage({\n label,\n width,\n height,\n alt,\n}: SampleImageOptions): MediaDoc {\n const fontSize = Math.max(12, Math.round(Math.min(width, height) / 12));\n const svg =\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\" viewBox=\"0 0 ${width} ${height}\">` +\n `<rect width=\"100%\" height=\"100%\" fill=\"#d4d4d8\"/>` +\n `<text x=\"50%\" y=\"50%\" dominant-baseline=\"middle\" text-anchor=\"middle\" ` +\n `font-family=\"system-ui, sans-serif\" font-size=\"${fontSize}\" fill=\"#52525b\">` +\n `${escapeXml(label)}</text></svg>`;\n return {\n url: `data:image/svg+xml,${encodeURIComponent(svg)}`,\n alt: alt ?? label,\n width,\n height,\n };\n}\n","import { sampleImage } from \"../../sample-image\";\nimport type { HeroBlockData } from \"../../types\";\n\n/**\n * The placeholder hero's sample. The hero family (BIS-45) ships a sample per\n * hero as each one is added; this is the one for the plain renderer.\n */\nexport const heroSample: HeroBlockData = {\n id: \"sample-hero\",\n blockType: \"hero\",\n eyebrow: \"Now booking spring appointments\",\n heading: \"Move well again, without the long wait\",\n body: \"Same-week assessments with a physiotherapist who stays with you from the first visit to the last. Most treatment plans finish in six to eight sessions.\",\n tone: \"sweep\",\n image: sampleImage({\n label: \"Hero background · 2000 × 1000\",\n width: 2000,\n height: 1000,\n alt: \"A bright, open treatment room\",\n }),\n links: [\n { label: \"Book an assessment\", href: \"/book\", newTab: false },\n {\n label: \"See our approach\",\n href: \"https://example.com/approach\",\n newTab: true,\n },\n ],\n};\n","import type { NapBlockData } from \"../../types\";\n\n/**\n * A fictional clinic. `emitJsonLd` is off: the block emits schema.org\n * `LocalBusiness` by default, and a preview must not put a business that does\n * not exist into a site's structured data. The phone numbers are in the\n * 555-01xx range reserved for fiction.\n */\nexport const napSample: NapBlockData = {\n id: \"sample-nap\",\n blockType: \"nap\",\n businessName: \"Larkspur Physiotherapy\",\n businessType: \"Physiotherapy\",\n address: {\n streetAddress: \"400 Larkspur Lane, Suite 120\",\n addressLocality: \"Boulder\",\n addressRegion: \"CO\",\n postalCode: \"80302\",\n addressCountry: \"US\",\n },\n departments: [\n {\n id: \"sample-dept-front-desk\",\n name: \"Front desk\",\n phoneE164: \"+13035550142\",\n phoneDisplay: \"(303) 555-0142\",\n },\n {\n id: \"sample-dept-billing\",\n name: \"Billing and insurance\",\n departmentType: \"AccountingService\",\n phoneE164: \"+13035550143\",\n },\n ],\n url: \"https://example.com\",\n showName: true,\n headingLevel: \"h2\",\n emitJsonLd: false,\n};\n","import { sampleImage } from \"../../sample-image\";\nimport type { ProcessStepsBlockData } from \"../../types\";\n\n/** Four steps: enough to show the rail advancing through a real sequence. */\nexport const processStepsSample: ProcessStepsBlockData = {\n id: \"sample-process-steps\",\n blockType: \"processSteps\",\n items: [\n {\n id: \"sample-step-assess\",\n title: \"Assessment\",\n description:\n \"A fifty-minute first visit: your history, a movement screen, and a clear explanation of what we found.\",\n image: sampleImage({\n label: \"Step 1 · 1600 × 1000\",\n width: 1600,\n height: 1000,\n alt: \"A physiotherapist taking notes during an assessment\",\n }),\n },\n {\n id: \"sample-step-plan\",\n title: \"Your plan\",\n description:\n \"A written plan with the number of sessions we expect, what each one is for, and the exercises between them.\",\n image: sampleImage({\n label: \"Step 2 · 1600 × 1000\",\n width: 1600,\n height: 1000,\n alt: \"A printed treatment plan on a desk\",\n }),\n },\n {\n id: \"sample-step-treat\",\n title: \"Treatment\",\n description:\n \"Hands-on work where it helps, and progressive loading where it matters. You leave every session knowing what to do next.\",\n image: sampleImage({\n label: \"Step 3 · 1600 × 1000\",\n width: 1600,\n height: 1000,\n alt: \"A patient lifting a light kettlebell under supervision\",\n }),\n },\n {\n id: \"sample-step-discharge\",\n title: \"Discharge and beyond\",\n description:\n \"A final review, a maintenance programme, and an open door if anything flares up later.\",\n image: sampleImage({\n label: \"Step 4 · 1600 × 1000\",\n width: 1600,\n height: 1000,\n alt: \"A patient walking out of the clinic\",\n }),\n },\n ],\n autoAdvance: true,\n autoAdvanceDuration: 5000,\n pauseOnHover: true,\n defaultActiveIndex: 0,\n};\n","import type { RichTextBlockData, RichTextContent } from \"../../types\";\n\n/**\n * A serialized Lexical document, written by hand in the shapes the default\n * JSX converters read (`heading`, `paragraph`, `text`, `list`, `link`).\n * `version` is on every serialized Lexical node, and `direction`, `format` and\n * `indent` are what `RichTextContent` requires on the root; the converters\n * read none of them, and the sample carries them so it is shaped like a row\n * the editor saved.\n */\nconst block = {\n direction: \"ltr\" as const,\n format: \"\",\n indent: 0,\n version: 1,\n};\n\nfunction text(value: string, format = 0) {\n return {\n type: \"text\",\n text: value,\n format,\n detail: 0,\n mode: \"normal\",\n style: \"\",\n version: 1,\n };\n}\n\nfunction paragraph(children: unknown[]) {\n return { type: \"paragraph\", children, textFormat: 0, textStyle: \"\", ...block };\n}\n\nconst BOLD = 1;\n\nconst content: RichTextContent = {\n root: {\n type: \"root\",\n ...block,\n children: [\n {\n type: \"heading\",\n tag: \"h2\",\n children: [text(\"What to expect at your first visit\")],\n ...block,\n },\n paragraph([\n text(\"Your first appointment runs about \"),\n text(\"fifty minutes\", BOLD),\n text(\n \". We start with a conversation about what brought you in, then a movement assessment, and you leave with a written plan and the first two exercises.\",\n ),\n ]),\n paragraph([\n text(\"Bring comfortable clothes and any recent imaging. If you have questions before you arrive, \"),\n {\n type: \"link\",\n fields: { url: \"https://example.com/contact\", newTab: true, linkType: \"custom\" },\n children: [text(\"get in touch\")],\n ...block,\n version: 3,\n },\n text(\".\"),\n ]),\n {\n type: \"list\",\n listType: \"bullet\",\n tag: \"ul\",\n start: 1,\n children: [\n \"Assessment and written plan\",\n \"Hands-on treatment where it helps\",\n \"Exercises you can do at home, with video\",\n ].map((item, i) => ({\n type: \"listitem\",\n value: i + 1,\n children: [text(item)],\n ...block,\n })),\n ...block,\n },\n ],\n },\n};\n\nexport const richTextSample: RichTextBlockData = {\n id: \"sample-rich-text\",\n blockType: \"richText\",\n content,\n};\n","import { sampleImage } from \"../../sample-image\";\nimport type { ShowcasePanelsBlockData } from \"../../types\";\n\n/** Three panels: the minimum the layout is designed around. */\nexport const showcasePanelsSample: ShowcasePanelsBlockData = {\n id: \"sample-showcase-panels\",\n blockType: \"showcasePanels\",\n items: [\n {\n id: \"sample-panel-back\",\n title: \"Back and neck pain\",\n summary:\n \"Most back pain settles with the right movement, not rest. We find what is driving yours and build a plan around your week, not ours.\",\n image: sampleImage({\n label: \"Panel 1 · 1400 × 1000\",\n width: 1400,\n height: 1000,\n alt: \"A physiotherapist guiding a patient through a stretch\",\n }),\n href: \"/services/back-and-neck\",\n // Matches what automatic numbering would show, so the preview does not\n // mislead; it is here to exercise the override.\n numeral: \"01\",\n },\n {\n id: \"sample-panel-sport\",\n title: \"Sports injuries\",\n summary:\n \"From a rolled ankle to a post-surgical knee: a return-to-play plan with clear milestones, so you know when you are ready.\",\n image: sampleImage({\n label: \"Panel 2 · 1400 × 1000\",\n width: 1400,\n height: 1000,\n alt: \"A runner mid-stride on a track\",\n }),\n href: \"/services/sports-injuries\",\n },\n {\n id: \"sample-panel-post-op\",\n title: \"Post-operative rehab\",\n summary:\n \"We work from your surgeon's protocol and keep them in the loop, so every stage of recovery is signed off before the next begins.\",\n image: sampleImage({\n label: \"Panel 3 · 1400 × 1000\",\n width: 1400,\n height: 1000,\n alt: \"A patient on a rehabilitation bike\",\n }),\n },\n ],\n spineVariant: \"numbered\",\n watermark: \"LARKSPUR\",\n defaultActiveIndex: 0,\n};\n","import { sampleImage } from \"../../sample-image\";\nimport type { TestimonialMasonryBlockData } from \"../../types\";\n\nfunction avatar(name: string) {\n return sampleImage({\n label: name\n .split(\" \")\n .map((part) => part[0])\n .join(\"\"),\n width: 160,\n height: 160,\n alt: `Portrait of ${name}`,\n });\n}\n\n/**\n * Six quotes, two of them without an avatar so the initials fallback shows.\n * `minItemsForFade` is lowered to six so the preview also shows the fade and\n * the link beneath it, which the default of seven would hide at this count.\n */\nexport const testimonialMasonrySample: TestimonialMasonryBlockData = {\n id: \"sample-testimonial-masonry\",\n blockType: \"testimonialMasonry\",\n eyebrow: \"From our patients\",\n title: \"What people say after they finish\",\n description:\n \"Every review here was left by a patient we discharged. We do not edit them.\",\n items: [\n {\n id: \"sample-quote-1\",\n content:\n \"I had written off running. Eight weeks later I did a parkrun, and the plan I was given actually fitted around a job and two kids.\",\n author: {\n name: \"Priya Natarajan\",\n title: \"Recovered from a hamstring tear\",\n avatar: avatar(\"Priya Natarajan\"),\n },\n },\n {\n id: \"sample-quote-2\",\n content:\n \"The first physio who explained what was wrong in words I understood.\",\n author: {\n name: \"Tom Ferreira\",\n title: \"Lower back pain\",\n avatar: avatar(\"Tom Ferreira\"),\n },\n },\n {\n id: \"sample-quote-3\",\n content:\n \"My surgeon said my knee was ahead of schedule at every check-in. That was the rehab, not me.\",\n author: { name: \"Aisha Bello\", title: \"ACL reconstruction\" },\n },\n {\n id: \"sample-quote-4\",\n content:\n \"Booked on a Tuesday, seen on the Thursday. The shoulder I had put up with for a year was sorted in six visits.\",\n author: {\n name: \"Marcus Whitfield\",\n title: \"Frozen shoulder\",\n avatar: avatar(\"Marcus Whitfield\"),\n },\n },\n {\n id: \"sample-quote-5\",\n content:\n \"They kept my consultant in the loop the whole way through, which nobody else had bothered to do.\",\n author: { name: \"Helen Ostrowski\" },\n },\n {\n id: \"sample-quote-6\",\n content:\n \"Honest about what would take time and what would not. I would send my parents here.\",\n author: {\n name: \"Daniel Kim\",\n title: \"Ankle sprain\",\n avatar: avatar(\"Daniel Kim\"),\n },\n },\n ],\n link: {\n label: \"Read every review\",\n href: \"https://example.com/reviews\",\n newTab: true,\n },\n minItemsForFade: 6,\n maxVisibleRows: 2,\n};\n","import { faqColumnsSample } from \"./blocks/faq-columns/sample\";\nimport { heroSample } from \"./blocks/hero/sample\";\nimport { napSample } from \"./blocks/nap/sample\";\nimport { processStepsSample } from \"./blocks/process-steps/sample\";\nimport { richTextSample } from \"./blocks/rich-text/sample\";\nimport { showcasePanelsSample } from \"./blocks/showcase-panels/sample\";\nimport { testimonialMasonrySample } from \"./blocks/testimonial-masonry/sample\";\nimport type { BisonBlockData, BisonBlockType } from \"./types\";\n\n/**\n * One sample row per block, each typed to its own row shape. Assignable to\n * `Record<BisonBlockType, BisonBlockData>` wherever the wider type is wanted.\n */\nexport type BlockSamples = {\n [K in BisonBlockType]: Extract<BisonBlockData, { blockType: K }>;\n};\n\n/**\n * Every block the package ships, rendered without a CMS row.\n *\n * A Block library page (BIS-52) renders these live so an admin can see each\n * block before enabling it. Each sample lives beside its block's `config.ts`\n * and `component.tsx` as `sample.ts`, and `src/__tests__/samples.test.tsx`\n * locks the contract: one entry per slug, `blockType` matching the key, every\n * config field exercised at least once, and every image self-contained.\n *\n * React-free on purpose: it is read from a Global's field config, which\n * Payload loads in plain Node like the rest of this entry, as well as from a\n * route handler. Only what this package ships is here; a site supplies samples\n * for its own blocks through the Block library factory, whose option BIS-52\n * names.\n */\nexport const blockSamples: BlockSamples = {\n hero: heroSample,\n richText: richTextSample,\n showcasePanels: showcasePanelsSample,\n processSteps: processStepsSample,\n faqColumns: faqColumnsSample,\n testimonialMasonry: testimonialMasonrySample,\n nap: napSample,\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bison-lab/payload-blocks",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Payload CMS block configs and renderers for the Bison Lab marketing blocks",
|
|
5
5
|
"homepage": "https://components.bisonlab.ai",
|
|
6
6
|
"repository": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"tsdown": "^0.21.0",
|
|
75
75
|
"typescript": "^5.9.3",
|
|
76
76
|
"vitest": "^4.1.2",
|
|
77
|
-
"@bison-lab/ui": "3.
|
|
77
|
+
"@bison-lab/ui": "3.1.0"
|
|
78
78
|
},
|
|
79
79
|
"publishConfig": {
|
|
80
80
|
"access": "public"
|