@brandlab-digital/site-next 3.10.0 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui-kit/brandlab-skin.d.ts +22 -0
- package/dist/ui-kit/brandlab-skin.d.ts.map +1 -1
- package/dist/ui-kit/brandlab-skin.js.map +1 -1
- package/dist/ui-kit/sections/accordion.d.ts +25 -0
- package/dist/ui-kit/sections/accordion.d.ts.map +1 -0
- package/dist/ui-kit/sections/accordion.js +14 -0
- package/dist/ui-kit/sections/accordion.js.map +1 -0
- package/dist/ui-kit/sections/contact-block.d.ts +51 -0
- package/dist/ui-kit/sections/contact-block.d.ts.map +1 -0
- package/dist/ui-kit/sections/contact-block.js +15 -0
- package/dist/ui-kit/sections/contact-block.js.map +1 -0
- package/dist/ui-kit/sections/cta-band.d.ts +59 -0
- package/dist/ui-kit/sections/cta-band.d.ts.map +1 -0
- package/dist/ui-kit/sections/cta-band.js +11 -0
- package/dist/ui-kit/sections/cta-band.js.map +1 -0
- package/dist/ui-kit/sections/faqs.d.ts +43 -0
- package/dist/ui-kit/sections/faqs.d.ts.map +1 -0
- package/dist/ui-kit/sections/faqs.js +40 -0
- package/dist/ui-kit/sections/faqs.js.map +1 -0
- package/dist/ui-kit/sections/index.d.ts +13 -0
- package/dist/ui-kit/sections/index.d.ts.map +1 -0
- package/dist/ui-kit/sections/index.js +13 -0
- package/dist/ui-kit/sections/index.js.map +1 -0
- package/dist/ui-kit/sections/page-header.d.ts +29 -0
- package/dist/ui-kit/sections/page-header.d.ts.map +1 -0
- package/dist/ui-kit/sections/page-header.js +9 -0
- package/dist/ui-kit/sections/page-header.js.map +1 -0
- package/dist/ui-kit/sections/price-guide.d.ts +56 -0
- package/dist/ui-kit/sections/price-guide.d.ts.map +1 -0
- package/dist/ui-kit/sections/price-guide.js +28 -0
- package/dist/ui-kit/sections/price-guide.js.map +1 -0
- package/dist/ui-kit/sections/price-table.d.ts +44 -0
- package/dist/ui-kit/sections/price-table.d.ts.map +1 -0
- package/dist/ui-kit/sections/price-table.js +29 -0
- package/dist/ui-kit/sections/price-table.js.map +1 -0
- package/dist/ui-kit/sections/proof-strip.d.ts +57 -0
- package/dist/ui-kit/sections/proof-strip.d.ts.map +1 -0
- package/dist/ui-kit/sections/proof-strip.js +20 -0
- package/dist/ui-kit/sections/proof-strip.js.map +1 -0
- package/dist/ui-kit/sections/rating-chip.d.ts +25 -0
- package/dist/ui-kit/sections/rating-chip.d.ts.map +1 -0
- package/dist/ui-kit/sections/rating-chip.js +15 -0
- package/dist/ui-kit/sections/rating-chip.js.map +1 -0
- package/dist/ui-kit/sections/reviews-band.d.ts +110 -0
- package/dist/ui-kit/sections/reviews-band.d.ts.map +1 -0
- package/dist/ui-kit/sections/reviews-band.js +104 -0
- package/dist/ui-kit/sections/reviews-band.js.map +1 -0
- package/dist/ui-kit/sections/section-sequence.d.ts +39 -0
- package/dist/ui-kit/sections/section-sequence.d.ts.map +1 -0
- package/dist/ui-kit/sections/section-sequence.js +34 -0
- package/dist/ui-kit/sections/section-sequence.js.map +1 -0
- package/dist/ui-kit/sections/site-footer-columns.d.ts +12 -0
- package/dist/ui-kit/sections/site-footer-columns.d.ts.map +1 -0
- package/dist/ui-kit/sections/site-footer-columns.js +63 -0
- package/dist/ui-kit/sections/site-footer-columns.js.map +1 -0
- package/dist/ui-kit/sections/site-footer.d.ts +59 -0
- package/dist/ui-kit/sections/site-footer.d.ts.map +1 -0
- package/dist/ui-kit/sections/site-footer.js +41 -0
- package/dist/ui-kit/sections/site-footer.js.map +1 -0
- package/dist/ui-kit/structure.css +569 -0
- package/package.json +6 -2
|
@@ -17,6 +17,17 @@ import type { PhotoUploadSkin } from './form/photo-upload.js';
|
|
|
17
17
|
import type { QuoteDialogSkin } from './form/quote-dialog.js';
|
|
18
18
|
import type { QuoteFormSkin } from './form/quote-form.js';
|
|
19
19
|
import type { ThankYouSkin } from './form/thank-you.js';
|
|
20
|
+
import type { CTABandSkin } from './sections/cta-band.js';
|
|
21
|
+
import type { RatingChipSkin } from './sections/rating-chip.js';
|
|
22
|
+
import type { ReviewsBandSkin } from './sections/reviews-band.js';
|
|
23
|
+
import type { SiteFooterSkin } from './sections/site-footer.js';
|
|
24
|
+
import type { AccordionSkin } from './sections/accordion.js';
|
|
25
|
+
import type { ContactBlockSkin } from './sections/contact-block.js';
|
|
26
|
+
import type { FAQsSkin } from './sections/faqs.js';
|
|
27
|
+
import type { PageHeaderSkin } from './sections/page-header.js';
|
|
28
|
+
import type { PriceGuideSkin } from './sections/price-guide.js';
|
|
29
|
+
import type { PriceTableSkin } from './sections/price-table.js';
|
|
30
|
+
import type { ProofStripSkin } from './sections/proof-strip.js';
|
|
20
31
|
export interface BrandlabSkin {
|
|
21
32
|
readonly buttonLink?: ButtonLinkSkin;
|
|
22
33
|
readonly textLink?: TextLinkSkin;
|
|
@@ -35,5 +46,16 @@ export interface BrandlabSkin {
|
|
|
35
46
|
readonly dialog?: DialogSkin;
|
|
36
47
|
readonly field?: FieldSkin;
|
|
37
48
|
readonly thankYou?: ThankYouSkin;
|
|
49
|
+
readonly ratingChip?: RatingChipSkin;
|
|
50
|
+
readonly reviewsBand?: ReviewsBandSkin;
|
|
51
|
+
readonly ctaBand?: CTABandSkin;
|
|
52
|
+
readonly siteFooter?: SiteFooterSkin;
|
|
53
|
+
readonly accordion?: AccordionSkin;
|
|
54
|
+
readonly faqs?: FAQsSkin;
|
|
55
|
+
readonly priceTable?: PriceTableSkin;
|
|
56
|
+
readonly priceGuide?: PriceGuideSkin;
|
|
57
|
+
readonly proofStrip?: ProofStripSkin;
|
|
58
|
+
readonly pageHeader?: PageHeaderSkin;
|
|
59
|
+
readonly contactBlock?: ContactBlockSkin;
|
|
38
60
|
}
|
|
39
61
|
//# sourceMappingURL=brandlab-skin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brandlab-skin.d.ts","sourceRoot":"","sources":["../../src/ui-kit/brandlab-skin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACxF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"brandlab-skin.d.ts","sourceRoot":"","sources":["../../src/ui-kit/brandlab-skin.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brandlab-skin.js","sourceRoot":"","sources":["../../src/ui-kit/brandlab-skin.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * A site's whole look for the ui-kit: one entry per shared component it renders (`src/site/skin.ts`, written with\n * `satisfies BrandlabSkin`). Each release adds the entries for its components. K2 checks that every rendered\n * component has an entry and that no reference-skin class ships.\n */\nimport type { ButtonLinkSkin, PhoneLinkSkin, TextLinkSkin } from './primitives/links.js'\nimport type { QuoteButtonSkin } from './primitives/quote-button.js'\nimport type { ContainerSkin, SectionHeadingSkin } from './primitives/layout.js'\nimport type { BackToTopSkin } from './shell/back-to-top.js'\nimport type { MobileCallBarSkin } from './shell/mobile-call-bar.js'\nimport type { MobileDrawerSkin } from './shell/mobile-drawer.js'\nimport type { SiteHeaderSkin } from './shell/site-header.js'\nimport type { UtilityBarSkin } from './shell/utility-bar.js'\nimport type { DialogSkin } from './form/dialog.js'\nimport type { FieldSkin } from './form/field.js'\nimport type { PhotoUploadSkin } from './form/photo-upload.js'\nimport type { QuoteDialogSkin } from './form/quote-dialog.js'\nimport type { QuoteFormSkin } from './form/quote-form.js'\nimport type { ThankYouSkin } from './form/thank-you.js'\n\nexport interface BrandlabSkin {\n readonly buttonLink?: ButtonLinkSkin\n readonly textLink?: TextLinkSkin\n readonly phoneLink?: PhoneLinkSkin\n readonly quoteButton?: QuoteButtonSkin\n readonly container?: ContainerSkin\n readonly sectionHeading?: SectionHeadingSkin\n readonly siteHeader?: SiteHeaderSkin\n readonly mobileDrawer?: MobileDrawerSkin\n readonly utilityBar?: UtilityBarSkin\n readonly mobileCallBar?: MobileCallBarSkin\n readonly backToTop?: BackToTopSkin\n // form (3.10.0)\n readonly quoteForm?: QuoteFormSkin\n readonly quoteDialog?: QuoteDialogSkin\n readonly photoUpload?: PhotoUploadSkin\n readonly dialog?: DialogSkin\n readonly field?: FieldSkin\n readonly thankYou?: ThankYouSkin\n}\n"]}
|
|
1
|
+
{"version":3,"file":"brandlab-skin.js","sourceRoot":"","sources":["../../src/ui-kit/brandlab-skin.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * A site's whole look for the ui-kit: one entry per shared component it renders (`src/site/skin.ts`, written with\n * `satisfies BrandlabSkin`). Each release adds the entries for its components. K2 checks that every rendered\n * component has an entry and that no reference-skin class ships.\n */\nimport type { ButtonLinkSkin, PhoneLinkSkin, TextLinkSkin } from './primitives/links.js'\nimport type { QuoteButtonSkin } from './primitives/quote-button.js'\nimport type { ContainerSkin, SectionHeadingSkin } from './primitives/layout.js'\nimport type { BackToTopSkin } from './shell/back-to-top.js'\nimport type { MobileCallBarSkin } from './shell/mobile-call-bar.js'\nimport type { MobileDrawerSkin } from './shell/mobile-drawer.js'\nimport type { SiteHeaderSkin } from './shell/site-header.js'\nimport type { UtilityBarSkin } from './shell/utility-bar.js'\nimport type { DialogSkin } from './form/dialog.js'\nimport type { FieldSkin } from './form/field.js'\nimport type { PhotoUploadSkin } from './form/photo-upload.js'\nimport type { QuoteDialogSkin } from './form/quote-dialog.js'\nimport type { QuoteFormSkin } from './form/quote-form.js'\nimport type { ThankYouSkin } from './form/thank-you.js'\nimport type { CTABandSkin } from './sections/cta-band.js'\nimport type { RatingChipSkin } from './sections/rating-chip.js'\nimport type { ReviewsBandSkin } from './sections/reviews-band.js'\nimport type { SiteFooterSkin } from './sections/site-footer.js'\nimport type { AccordionSkin } from './sections/accordion.js'\nimport type { ContactBlockSkin } from './sections/contact-block.js'\nimport type { FAQsSkin } from './sections/faqs.js'\nimport type { PageHeaderSkin } from './sections/page-header.js'\nimport type { PriceGuideSkin } from './sections/price-guide.js'\nimport type { PriceTableSkin } from './sections/price-table.js'\nimport type { ProofStripSkin } from './sections/proof-strip.js'\n\nexport interface BrandlabSkin {\n readonly buttonLink?: ButtonLinkSkin\n readonly textLink?: TextLinkSkin\n readonly phoneLink?: PhoneLinkSkin\n readonly quoteButton?: QuoteButtonSkin\n readonly container?: ContainerSkin\n readonly sectionHeading?: SectionHeadingSkin\n readonly siteHeader?: SiteHeaderSkin\n readonly mobileDrawer?: MobileDrawerSkin\n readonly utilityBar?: UtilityBarSkin\n readonly mobileCallBar?: MobileCallBarSkin\n readonly backToTop?: BackToTopSkin\n // form (3.10.0)\n readonly quoteForm?: QuoteFormSkin\n readonly quoteDialog?: QuoteDialogSkin\n readonly photoUpload?: PhotoUploadSkin\n readonly dialog?: DialogSkin\n readonly field?: FieldSkin\n readonly thankYou?: ThankYouSkin\n readonly ratingChip?: RatingChipSkin\n readonly reviewsBand?: ReviewsBandSkin\n readonly ctaBand?: CTABandSkin\n readonly siteFooter?: SiteFooterSkin\n readonly accordion?: AccordionSkin\n readonly faqs?: FAQsSkin\n readonly priceTable?: PriceTableSkin\n readonly priceGuide?: PriceGuideSkin\n readonly proofStrip?: ProofStripSkin\n readonly pageHeader?: PageHeaderSkin\n readonly contactBlock?: ContactBlockSkin\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Accordion`: a list of disclosures on `<details name>`. Server component: items sharing a `name` open one at a time
|
|
3
|
+
* natively (no script), and every item's content is in the server HTML whether open or closed (K7). Items are closed
|
|
4
|
+
* by default; `defaultOpen` opens one only where a site recorded it (Gabrael: Eastern Suburbs open). The open state is
|
|
5
|
+
* the native `open` attribute, so skins style `[open]`.
|
|
6
|
+
*/
|
|
7
|
+
import type { ReactNode } from 'react';
|
|
8
|
+
import { type SkinEntry } from '../skin.js';
|
|
9
|
+
export type AccordionSlot = 'root' | 'item' | 'summary' | 'label' | 'marker' | 'content';
|
|
10
|
+
export type AccordionSkin = SkinEntry<AccordionSlot>;
|
|
11
|
+
export interface AccordionItem {
|
|
12
|
+
/** A stable slug, unique in the group. The item's element id is `{name}-{id}`, so it can be linked to. */
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly summary: ReactNode;
|
|
15
|
+
readonly content: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
export interface AccordionProps extends AccordionSkin {
|
|
18
|
+
readonly items: readonly AccordionItem[];
|
|
19
|
+
/** The group: items sharing a name open one at a time. Unique on the page. */
|
|
20
|
+
readonly name: string;
|
|
21
|
+
/** The id of the item open on load, only when the site recorded one. */
|
|
22
|
+
readonly defaultOpen?: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
export declare function Accordion({ items, name, defaultOpen, classes }: AccordionProps): import("react/jsx-runtime").JSX.Element | null;
|
|
25
|
+
//# sourceMappingURL=accordion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accordion.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/accordion.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AAEjD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAA;AACxF,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,CAAC,CAAA;AAEpD,MAAM,WAAW,aAAa;IAC5B,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAA;CAC5B;AAED,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAA;IACxC,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,wEAAwE;IACxE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC1C;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,cAAc,kDAoB9E"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Icon } from '../icons.js';
|
|
3
|
+
import { slot } from '../skin.js';
|
|
4
|
+
export function Accordion({ items, name, defaultOpen, classes }) {
|
|
5
|
+
if (items.length === 0)
|
|
6
|
+
return null;
|
|
7
|
+
const ids = new Set(items.map((item) => item.id));
|
|
8
|
+
if (ids.size !== items.length)
|
|
9
|
+
throw new Error(`Accordion "${name}": item ids must be unique.`);
|
|
10
|
+
if (defaultOpen !== undefined && !ids.has(defaultOpen))
|
|
11
|
+
throw new Error(`Accordion "${name}": defaultOpen "${defaultOpen}" is not an item.`);
|
|
12
|
+
return (_jsx("div", { "data-bl": "accordion", className: classes?.root, children: items.map((item) => (_jsxs("details", { id: `${name}-${item.id}`, name: name, open: item.id === defaultOpen, ...slot(classes, 'item'), children: [_jsxs("summary", { ...slot(classes, 'summary'), children: [_jsx("span", { ...slot(classes, 'label'), children: item.summary }), _jsx("span", { ...slot(classes, 'marker'), "aria-hidden": "true", children: _jsx(Icon, { name: "chevron" }) })] }), _jsx("div", { ...slot(classes, 'content'), children: item.content })] }, item.id))) }));
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=accordion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accordion.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/accordion.tsx"],"names":[],"mappings":";AAOA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAClC,OAAO,EAAE,IAAI,EAAkB,MAAM,YAAY,CAAA;AAoBjD,MAAM,UAAU,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAkB;IAC7E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACnC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;IACjD,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,6BAA6B,CAAC,CAAA;IAC/F,IAAI,WAAW,KAAK,SAAS,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,mBAAmB,WAAW,mBAAmB,CAAC,CAAA;IAC5I,OAAO,CACL,yBAAa,WAAW,EAAC,SAAS,EAAE,OAAO,EAAE,IAAI,YAC9C,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnB,mBAAuB,EAAE,EAAE,GAAG,IAAI,IAAI,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,KAAK,WAAW,KAAM,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,aACnH,sBAAa,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,aACnC,kBAAU,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,YAAG,IAAI,CAAC,OAAO,GAAQ,EACvD,kBAAU,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,iBAAc,MAAM,YACnD,KAAC,IAAI,IAAC,IAAI,EAAC,SAAS,GAAG,GAClB,IACC,EACV,iBAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,YAAG,IAAI,CAAC,OAAO,GAAO,KAP3C,IAAI,CAAC,EAAE,CAQX,CACX,CAAC,GACE,CACP,CAAA;AACH,CAAC","sourcesContent":["/**\n * `Accordion`: a list of disclosures on `<details name>`. Server component: items sharing a `name` open one at a time\n * natively (no script), and every item's content is in the server HTML whether open or closed (K7). Items are closed\n * by default; `defaultOpen` opens one only where a site recorded it (Gabrael: Eastern Suburbs open). The open state is\n * the native `open` attribute, so skins style `[open]`.\n */\nimport type { ReactNode } from 'react'\nimport { Icon } from '../icons.js'\nimport { slot, type SkinEntry } from '../skin.js'\n\nexport type AccordionSlot = 'root' | 'item' | 'summary' | 'label' | 'marker' | 'content'\nexport type AccordionSkin = SkinEntry<AccordionSlot>\n\nexport interface AccordionItem {\n /** A stable slug, unique in the group. The item's element id is `{name}-{id}`, so it can be linked to. */\n readonly id: string\n readonly summary: ReactNode\n readonly content: ReactNode\n}\n\nexport interface AccordionProps extends AccordionSkin {\n readonly items: readonly AccordionItem[]\n /** The group: items sharing a name open one at a time. Unique on the page. */\n readonly name: string\n /** The id of the item open on load, only when the site recorded one. */\n readonly defaultOpen?: string | undefined\n}\n\nexport function Accordion({ items, name, defaultOpen, classes }: AccordionProps) {\n if (items.length === 0) return null\n const ids = new Set(items.map((item) => item.id))\n if (ids.size !== items.length) throw new Error(`Accordion \"${name}\": item ids must be unique.`)\n if (defaultOpen !== undefined && !ids.has(defaultOpen)) throw new Error(`Accordion \"${name}\": defaultOpen \"${defaultOpen}\" is not an item.`)\n return (\n <div data-bl=\"accordion\" className={classes?.root}>\n {items.map((item) => (\n <details key={item.id} id={`${name}-${item.id}`} name={name} open={item.id === defaultOpen} {...slot(classes, 'item')}>\n <summary {...slot(classes, 'summary')}>\n <span {...slot(classes, 'label')}>{item.summary}</span>\n <span {...slot(classes, 'marker')} aria-hidden=\"true\">\n <Icon name=\"chevron\" />\n </span>\n </summary>\n <div {...slot(classes, 'content')}>{item.content}</div>\n </details>\n ))}\n </div>\n )\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ContactBlock` (S25, `contact`): the contact page itself. Server component. The site's quote form on one side
|
|
3
|
+
* (the start by default: form left, details right) and the contact details on the other: phone (placement
|
|
4
|
+
* `contact`), email, hours, the public address only where the site passes it, licences and areas. Nothing else: it
|
|
5
|
+
* takes no other content, and the page is the page header then this block (K12-contact). The form side always has
|
|
6
|
+
* `id="quote"`, the target of `QuoteButton`'s no-script fallback (`/contact#quote`).
|
|
7
|
+
*/
|
|
8
|
+
import type { ReactNode } from 'react';
|
|
9
|
+
import { type SectionTone } from '../primitives/layout.js';
|
|
10
|
+
import { type SkinEntry } from '../skin.js';
|
|
11
|
+
import type { UiLink, UiPhone } from '../types.js';
|
|
12
|
+
/** `phone` and `link` are the classes for the nested `PhoneLink` and the email and areas links. */
|
|
13
|
+
export type ContactBlockSlot = 'root' | 'inner' | 'form' | 'details' | 'heading' | 'list' | 'item' | 'label' | 'value' | 'line' | 'phone' | 'link';
|
|
14
|
+
export type ContactBlockSkin = SkinEntry<ContactBlockSlot, never, {
|
|
15
|
+
formSide?: 'start' | 'end';
|
|
16
|
+
}>;
|
|
17
|
+
/** A labelled detail of one or more lines, for example hours or a licence. */
|
|
18
|
+
export interface ContactDetailLines {
|
|
19
|
+
readonly label: string;
|
|
20
|
+
readonly lines: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
export interface ContactBlockProps extends ContactBlockSkin {
|
|
23
|
+
/** The details' H2, for example "Contact details". */
|
|
24
|
+
readonly title: ReactNode;
|
|
25
|
+
/** The site's quote form (`QuoteForm`, plain). */
|
|
26
|
+
readonly form: ReactNode;
|
|
27
|
+
/** From the derived business facts; never typed by hand (C10). */
|
|
28
|
+
readonly phone?: {
|
|
29
|
+
readonly label: string;
|
|
30
|
+
readonly phone: UiPhone;
|
|
31
|
+
};
|
|
32
|
+
readonly email?: {
|
|
33
|
+
readonly label: string;
|
|
34
|
+
readonly address: string;
|
|
35
|
+
};
|
|
36
|
+
readonly hours?: ContactDetailLines;
|
|
37
|
+
/** The public address, only where it shows (customers visit, and Romie said so). Never derived here. */
|
|
38
|
+
readonly address?: ContactDetailLines;
|
|
39
|
+
readonly licences?: ContactDetailLines;
|
|
40
|
+
readonly areas?: {
|
|
41
|
+
readonly label: string;
|
|
42
|
+
readonly text: string;
|
|
43
|
+
readonly link?: UiLink;
|
|
44
|
+
};
|
|
45
|
+
/** Defaults to `contact-heading`. */
|
|
46
|
+
readonly headingId?: string;
|
|
47
|
+
/** Set by `SectionSequence`. */
|
|
48
|
+
readonly tone?: SectionTone;
|
|
49
|
+
}
|
|
50
|
+
export declare function ContactBlock({ title, form, phone, email, hours, address, licences, areas, headingId, tone, layout, classes, }: ContactBlockProps): import("react/jsx-runtime").JSX.Element;
|
|
51
|
+
//# sourceMappingURL=contact-block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contact-block.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/contact-block.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAEnE,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAElD,mGAAmG;AACnG,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,OAAO,GACP,MAAM,GACN,SAAS,GACT,SAAS,GACT,MAAM,GACN,MAAM,GACN,OAAO,GACP,OAAO,GACP,MAAM,GACN,OAAO,GACP,MAAM,CAAA;AACV,MAAM,MAAM,gBAAgB,GAAG,SAAS,CAAC,gBAAgB,EAAE,KAAK,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;CAAE,CAAC,CAAA;AAEjG,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC;AAED,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACzD,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;IACpE,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IACrE,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB,CAAA;IACnC,wGAAwG;IACxG,QAAQ,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;IACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1F,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,gCAAgC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAA;CAC5B;AAID,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,IAAI,EACJ,KAAK,EACL,KAAK,EACL,KAAK,EACL,OAAO,EACP,QAAQ,EACR,KAAK,EACL,SAA6B,EAC7B,IAAI,EACJ,MAAM,EACN,OAAO,GACR,EAAE,iBAAiB,2CA4DnB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Section } from '../primitives/layout.js';
|
|
3
|
+
import { PhoneLink, TextLink } from '../primitives/links.js';
|
|
4
|
+
import { slot } from '../skin.js';
|
|
5
|
+
const EMAIL = /^[^\s@<>()]+@[^\s@<>()]+\.[^\s@<>()]+$/;
|
|
6
|
+
export function ContactBlock({ title, form, phone, email, hours, address, licences, areas, headingId = 'contact-heading', tone, layout, classes, }) {
|
|
7
|
+
if (email && !EMAIL.test(email.address))
|
|
8
|
+
throw new Error(`ContactBlock: "${email.address}" is not an email address.`);
|
|
9
|
+
const item = (key, label, value) => (_jsxs("div", { ...slot(classes, 'item'), "data-item": key, children: [_jsx("dt", { ...slot(classes, 'label'), children: label }), _jsx("dd", { ...slot(classes, 'value'), children: value })] }, key));
|
|
10
|
+
const lines = (detail) => detail.lines.map((line) => (_jsx("span", { ...slot(classes, 'line'), children: line }, line)));
|
|
11
|
+
return (_jsxs(Section, { id: "contact", labelledBy: headingId, tone: tone, classes: classes, phonePlacement: "contact", hooks: { 'data-form-side': layout?.formSide ?? 'start' }, children: [_jsx("div", { id: "quote", ...slot(classes, 'form'), children: form }), _jsxs("div", { ...slot(classes, 'details'), children: [_jsx("h2", { id: headingId, ...slot(classes, 'heading'), children: title }), phone || email || hours || address || licences || areas ? (_jsxs("dl", { ...slot(classes, 'list'), children: [phone ? item('phone', phone.label, _jsx(PhoneLink, { phone: phone.phone, placement: "contact", classes: { root: classes?.phone } })) : null, email ? item('email', email.label, _jsx("a", { href: `mailto:${email.address}`, ...slot(classes, 'link'), children: email.address })) : null, hours ? item('hours', hours.label, lines(hours)) : null, address ? item('address', address.label, lines(address)) : null, licences ? item('licences', licences.label, lines(licences)) : null, areas
|
|
12
|
+
? item('areas', areas.label, _jsxs(_Fragment, { children: [_jsx("span", { ...slot(classes, 'line'), children: areas.text }), areas.link ? (_jsx("span", { ...slot(classes, 'line'), children: _jsx(TextLink, { href: areas.link.href, classes: { root: classes?.link }, children: areas.link.label }) })) : null] }))
|
|
13
|
+
: null] })) : null] })] }));
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=contact-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contact-block.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/contact-block.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * `ContactBlock` (S25, `contact`): the contact page itself. Server component. The site's quote form on one side\n * (the start by default: form left, details right) and the contact details on the other: phone (placement\n * `contact`), email, hours, the public address only where the site passes it, licences and areas. Nothing else: it\n * takes no other content, and the page is the page header then this block (K12-contact). The form side always has\n * `id=\"quote\"`, the target of `QuoteButton`'s no-script fallback (`/contact#quote`).\n */\nimport type { ReactNode } from 'react'\nimport { Section, type SectionTone } from '../primitives/layout.js'\nimport { PhoneLink, TextLink } from '../primitives/links.js'\nimport { slot, type SkinEntry } from '../skin.js'\nimport type { UiLink, UiPhone } from '../types.js'\n\n/** `phone` and `link` are the classes for the nested `PhoneLink` and the email and areas links. */\nexport type ContactBlockSlot =\n | 'root'\n | 'inner'\n | 'form'\n | 'details'\n | 'heading'\n | 'list'\n | 'item'\n | 'label'\n | 'value'\n | 'line'\n | 'phone'\n | 'link'\nexport type ContactBlockSkin = SkinEntry<ContactBlockSlot, never, { formSide?: 'start' | 'end' }>\n\n/** A labelled detail of one or more lines, for example hours or a licence. */\nexport interface ContactDetailLines {\n readonly label: string\n readonly lines: readonly string[]\n}\n\nexport interface ContactBlockProps extends ContactBlockSkin {\n /** The details' H2, for example \"Contact details\". */\n readonly title: ReactNode\n /** The site's quote form (`QuoteForm`, plain). */\n readonly form: ReactNode\n /** From the derived business facts; never typed by hand (C10). */\n readonly phone?: { readonly label: string; readonly phone: UiPhone }\n readonly email?: { readonly label: string; readonly address: string }\n readonly hours?: ContactDetailLines\n /** The public address, only where it shows (customers visit, and Romie said so). Never derived here. */\n readonly address?: ContactDetailLines\n readonly licences?: ContactDetailLines\n readonly areas?: { readonly label: string; readonly text: string; readonly link?: UiLink }\n /** Defaults to `contact-heading`. */\n readonly headingId?: string\n /** Set by `SectionSequence`. */\n readonly tone?: SectionTone\n}\n\nconst EMAIL = /^[^\\s@<>()]+@[^\\s@<>()]+\\.[^\\s@<>()]+$/\n\nexport function ContactBlock({\n title,\n form,\n phone,\n email,\n hours,\n address,\n licences,\n areas,\n headingId = 'contact-heading',\n tone,\n layout,\n classes,\n}: ContactBlockProps) {\n if (email && !EMAIL.test(email.address)) throw new Error(`ContactBlock: \"${email.address}\" is not an email address.`)\n const item = (key: string, label: string, value: ReactNode) => (\n <div key={key} {...slot(classes, 'item')} data-item={key}>\n <dt {...slot(classes, 'label')}>{label}</dt>\n <dd {...slot(classes, 'value')}>{value}</dd>\n </div>\n )\n const lines = (detail: ContactDetailLines) =>\n detail.lines.map((line) => (\n <span key={line} {...slot(classes, 'line')}>\n {line}\n </span>\n ))\n return (\n <Section\n id=\"contact\"\n labelledBy={headingId}\n tone={tone}\n classes={classes}\n phonePlacement=\"contact\"\n hooks={{ 'data-form-side': layout?.formSide ?? 'start' }}\n >\n <div id=\"quote\" {...slot(classes, 'form')}>\n {form}\n </div>\n <div {...slot(classes, 'details')}>\n <h2 id={headingId} {...slot(classes, 'heading')}>\n {title}\n </h2>\n {phone || email || hours || address || licences || areas ? (\n <dl {...slot(classes, 'list')}>\n {phone ? item('phone', phone.label, <PhoneLink phone={phone.phone} placement=\"contact\" classes={{ root: classes?.phone }} />) : null}\n {email ? item('email', email.label, <a href={`mailto:${email.address}`} {...slot(classes, 'link')}>\n {email.address}\n </a>) : null}\n {hours ? item('hours', hours.label, lines(hours)) : null}\n {address ? item('address', address.label, lines(address)) : null}\n {licences ? item('licences', licences.label, lines(licences)) : null}\n {areas\n ? item(\n 'areas',\n areas.label,\n <>\n <span {...slot(classes, 'line')}>{areas.text}</span>\n {areas.link ? (\n <span {...slot(classes, 'line')}>\n <TextLink href={areas.link.href} classes={{ root: classes?.link }}>\n {areas.link.label}\n </TextLink>\n </span>\n ) : null}\n </>,\n )\n : null}\n </dl>\n ) : null}\n </div>\n </Section>\n )\n}\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S8 `CTABand` (`cta`): the one closing action design a site uses on every page. Server component, no client JS of
|
|
3
|
+
* its own. A heading, an optional one-line summary, the quote button (`lead_cta_click` placement `section`, id
|
|
4
|
+
* `quote-cta`) and one phone link (`phone_link_click` placement `cta`), phone first like the header, hero and call bar. No "what happens next" filler.
|
|
5
|
+
*
|
|
6
|
+
* Variants: `plain`; `photo`, with the site's backdrop image in `media` (decorative, so it renders `aria-hidden`);
|
|
7
|
+
* `form`, with the site's own quote form in `form` beside the heading and the phone link (the form is the quote
|
|
8
|
+
* action there, so the quote button is left out). A `photo` band without `media` or a `form` band without `form`
|
|
9
|
+
* renders as `plain`.
|
|
10
|
+
*
|
|
11
|
+
* Placement (S8): on every page except contact, thank-you and 404, which already end in the form or a confirmation.
|
|
12
|
+
* One variant per site, recorded in the skin.
|
|
13
|
+
*/
|
|
14
|
+
import type { ReactNode } from 'react';
|
|
15
|
+
import type { ButtonVariant } from '../primitives/links.js';
|
|
16
|
+
import type { SectionTone } from '../primitives/layout.js';
|
|
17
|
+
import { type SkinEntry } from '../skin.js';
|
|
18
|
+
import type { UiPhone } from '../types.js';
|
|
19
|
+
export declare const CTA_BAND_VARIANTS: readonly ["plain", "photo", "form"];
|
|
20
|
+
export type CTABandVariant = (typeof CTA_BAND_VARIANTS)[number];
|
|
21
|
+
export type CTABandSlot = 'root' | 'media' | 'inner' | 'content' | 'heading' | 'summary' | 'actions' | 'quote' | 'phone' | 'form';
|
|
22
|
+
export interface CTABandLayout {
|
|
23
|
+
readonly align?: 'start' | 'center';
|
|
24
|
+
/** The quote button's variant (default `primary`). */
|
|
25
|
+
readonly quoteVariant?: ButtonVariant;
|
|
26
|
+
/** The phone link's variant (default `button`). */
|
|
27
|
+
readonly phoneVariant?: 'text' | 'button';
|
|
28
|
+
}
|
|
29
|
+
export type CTABandSkin = SkinEntry<CTABandSlot, CTABandVariant, CTABandLayout>;
|
|
30
|
+
export declare const CTA_BAND_HEADING_ID = "bl-cta-heading";
|
|
31
|
+
export interface CTABandProps extends CTABandSkin {
|
|
32
|
+
/** The H2 text the site owns. */
|
|
33
|
+
readonly heading: string;
|
|
34
|
+
/** One line under the heading. */
|
|
35
|
+
readonly summary?: string;
|
|
36
|
+
/**
|
|
37
|
+
* The site's recorded quote label (C5) and the quote form it falls back to, for example `/contact#quote`. Required
|
|
38
|
+
* for the `plain` and `photo` variants; the `form` variant leaves the quote button out, so it may omit this.
|
|
39
|
+
*/
|
|
40
|
+
readonly quote?: {
|
|
41
|
+
readonly label: string;
|
|
42
|
+
readonly href: string;
|
|
43
|
+
};
|
|
44
|
+
readonly phone: UiPhone;
|
|
45
|
+
/** Optional words before the number, for example "Call". */
|
|
46
|
+
readonly phoneLabel?: string;
|
|
47
|
+
/** The `photo` variant's backdrop: the site's image node (decorative). */
|
|
48
|
+
readonly media?: ReactNode;
|
|
49
|
+
/** The `form` variant's form: the site's own quote form. */
|
|
50
|
+
readonly form?: ReactNode;
|
|
51
|
+
/** The id of the band's H2 (default `bl-cta-heading`). */
|
|
52
|
+
readonly headingId?: string;
|
|
53
|
+
/** Set by `SectionSequence`; a `photo` band defaults to `photo`. */
|
|
54
|
+
readonly tone?: SectionTone;
|
|
55
|
+
/** Signature art, rendered `aria-hidden`. */
|
|
56
|
+
readonly decoration?: ReactNode;
|
|
57
|
+
}
|
|
58
|
+
export declare function CTABand({ heading, summary, quote, phone, phoneLabel, media, form, headingId, tone, decoration, variant, layout, classes, }: CTABandProps): import("react/jsx-runtime").JSX.Element;
|
|
59
|
+
//# sourceMappingURL=cta-band.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cta-band.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/cta-band.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAE1D,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,eAAO,MAAM,iBAAiB,qCAAsC,CAAA;AACpE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/D,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAA;AAEjI,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;IACnC,sDAAsD;IACtD,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAA;IACrC,mDAAmD;IACnD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;CAC1C;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,EAAE,cAAc,EAAE,aAAa,CAAC,CAAA;AAE/E,eAAO,MAAM,mBAAmB,mBAAmB,CAAA;AAEnD,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,kCAAkC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAClE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAA;IAC1B,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAA;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAA;CAChC;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,OAAO,EACP,KAAK,EACL,KAAK,EACL,UAAU,EACV,KAAK,EACL,IAAI,EACJ,SAA+B,EAC/B,IAAI,EACJ,UAAU,EACV,OAAiB,EACjB,MAAM,EACN,OAAO,GACR,EAAE,YAAY,2CA6Cd"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PhoneLink } from '../primitives/links.js';
|
|
3
|
+
import { QuoteButton } from '../primitives/quote-button.js';
|
|
4
|
+
import { slot } from '../skin.js';
|
|
5
|
+
export const CTA_BAND_VARIANTS = ['plain', 'photo', 'form'];
|
|
6
|
+
export const CTA_BAND_HEADING_ID = 'bl-cta-heading';
|
|
7
|
+
export function CTABand({ heading, summary, quote, phone, phoneLabel, media, form, headingId = CTA_BAND_HEADING_ID, tone, decoration, variant = 'plain', layout, classes, }) {
|
|
8
|
+
const shown = (variant === 'photo' && !media) || (variant === 'form' && !form) ? 'plain' : variant;
|
|
9
|
+
return (_jsxs("section", { "data-bl-section": "cta", "data-variant": shown, "data-tone": tone ?? (shown === 'photo' ? 'photo' : undefined), "data-align": layout?.align ?? 'start', "aria-labelledby": headingId, className: classes?.root, children: [decoration ? (_jsx("div", { "data-slot": "decoration", "aria-hidden": "true", children: decoration })) : null, shown === 'photo' ? (_jsx("div", { ...slot(classes, 'media'), "aria-hidden": "true", children: media })) : null, _jsxs("div", { ...slot(classes, 'inner'), children: [_jsxs("div", { ...slot(classes, 'content'), children: [_jsx("h2", { id: headingId, ...slot(classes, 'heading'), children: heading }), summary ? _jsx("p", { ...slot(classes, 'summary'), children: summary }) : null, _jsxs("div", { ...slot(classes, 'actions'), children: [_jsx(PhoneLink, { phone: phone, placement: "cta", label: phoneLabel, showIcon: true, variant: layout?.phoneVariant ?? 'button', classes: { root: classes?.phone } }), shown === 'form' || !quote ? null : (_jsx(QuoteButton, { label: quote.label, href: quote.href, placement: "section", ctaId: "quote-cta", variant: layout?.quoteVariant ?? 'primary', classes: { root: classes?.quote } }))] })] }), shown === 'form' ? _jsx("div", { ...slot(classes, 'form'), children: form }) : null] })] }));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=cta-band.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cta-band.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/cta-band.tsx"],"names":[],"mappings":";AAcA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAGlD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAC3D,OAAO,EAAE,IAAI,EAAkB,MAAM,YAAY,CAAA;AAGjD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAU,CAAA;AAepE,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAA;AA2BnD,MAAM,UAAU,OAAO,CAAC,EACtB,OAAO,EACP,OAAO,EACP,KAAK,EACL,KAAK,EACL,UAAU,EACV,KAAK,EACL,IAAI,EACJ,SAAS,GAAG,mBAAmB,EAC/B,IAAI,EACJ,UAAU,EACV,OAAO,GAAG,OAAO,EACjB,MAAM,EACN,OAAO,GACM;IACb,MAAM,KAAK,GAAmB,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;IAClH,OAAO,CACL,sCACkB,KAAK,kBACP,KAAK,eACR,IAAI,IAAI,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,gBAChD,MAAM,EAAE,KAAK,IAAI,OAAO,qBACnB,SAAS,EAC1B,SAAS,EAAE,OAAO,EAAE,IAAI,aAEvB,UAAU,CAAC,CAAC,CAAC,CACZ,2BAAe,YAAY,iBAAa,MAAM,YAC3C,UAAU,GACP,CACP,CAAC,CAAC,CAAC,IAAI,EACP,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,CACnB,iBAAS,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,iBAAc,MAAM,YAChD,KAAK,GACF,CACP,CAAC,CAAC,CAAC,IAAI,EACR,kBAAS,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,aAC7B,kBAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,aAC/B,aAAI,EAAE,EAAE,SAAS,KAAM,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,YAC5C,OAAO,GACL,EACJ,OAAO,CAAC,CAAC,CAAC,eAAO,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,YAAG,OAAO,GAAK,CAAC,CAAC,CAAC,IAAI,EAChE,kBAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,aAC/B,KAAC,SAAS,IAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAC,KAAK,EAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,QAAC,OAAO,EAAE,MAAM,EAAE,YAAY,IAAI,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAI,EACpJ,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACnC,KAAC,WAAW,IACV,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,SAAS,EAAC,SAAS,EACnB,KAAK,EAAC,WAAW,EACjB,OAAO,EAAE,MAAM,EAAE,YAAY,IAAI,SAAS,EAC1C,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GACjC,CACH,IACG,IACF,EACL,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,iBAAS,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,YAAG,IAAI,GAAO,CAAC,CAAC,CAAC,IAAI,IACnE,IACE,CACX,CAAA;AACH,CAAC","sourcesContent":["/**\n * S8 `CTABand` (`cta`): the one closing action design a site uses on every page. Server component, no client JS of\n * its own. A heading, an optional one-line summary, the quote button (`lead_cta_click` placement `section`, id\n * `quote-cta`) and one phone link (`phone_link_click` placement `cta`), phone first like the header, hero and call bar. No \"what happens next\" filler.\n *\n * Variants: `plain`; `photo`, with the site's backdrop image in `media` (decorative, so it renders `aria-hidden`);\n * `form`, with the site's own quote form in `form` beside the heading and the phone link (the form is the quote\n * action there, so the quote button is left out). A `photo` band without `media` or a `form` band without `form`\n * renders as `plain`.\n *\n * Placement (S8): on every page except contact, thank-you and 404, which already end in the form or a confirmation.\n * One variant per site, recorded in the skin.\n */\nimport type { ReactNode } from 'react'\nimport { PhoneLink } from '../primitives/links.js'\nimport type { ButtonVariant } from '../primitives/links.js'\nimport type { SectionTone } from '../primitives/layout.js'\nimport { QuoteButton } from '../primitives/quote-button.js'\nimport { slot, type SkinEntry } from '../skin.js'\nimport type { UiPhone } from '../types.js'\n\nexport const CTA_BAND_VARIANTS = ['plain', 'photo', 'form'] as const\nexport type CTABandVariant = (typeof CTA_BAND_VARIANTS)[number]\n\nexport type CTABandSlot = 'root' | 'media' | 'inner' | 'content' | 'heading' | 'summary' | 'actions' | 'quote' | 'phone' | 'form'\n\nexport interface CTABandLayout {\n readonly align?: 'start' | 'center'\n /** The quote button's variant (default `primary`). */\n readonly quoteVariant?: ButtonVariant\n /** The phone link's variant (default `button`). */\n readonly phoneVariant?: 'text' | 'button'\n}\n\nexport type CTABandSkin = SkinEntry<CTABandSlot, CTABandVariant, CTABandLayout>\n\nexport const CTA_BAND_HEADING_ID = 'bl-cta-heading'\n\nexport interface CTABandProps extends CTABandSkin {\n /** The H2 text the site owns. */\n readonly heading: string\n /** One line under the heading. */\n readonly summary?: string\n /**\n * The site's recorded quote label (C5) and the quote form it falls back to, for example `/contact#quote`. Required\n * for the `plain` and `photo` variants; the `form` variant leaves the quote button out, so it may omit this.\n */\n readonly quote?: { readonly label: string; readonly href: string }\n readonly phone: UiPhone\n /** Optional words before the number, for example \"Call\". */\n readonly phoneLabel?: string\n /** The `photo` variant's backdrop: the site's image node (decorative). */\n readonly media?: ReactNode\n /** The `form` variant's form: the site's own quote form. */\n readonly form?: ReactNode\n /** The id of the band's H2 (default `bl-cta-heading`). */\n readonly headingId?: string\n /** Set by `SectionSequence`; a `photo` band defaults to `photo`. */\n readonly tone?: SectionTone\n /** Signature art, rendered `aria-hidden`. */\n readonly decoration?: ReactNode\n}\n\nexport function CTABand({\n heading,\n summary,\n quote,\n phone,\n phoneLabel,\n media,\n form,\n headingId = CTA_BAND_HEADING_ID,\n tone,\n decoration,\n variant = 'plain',\n layout,\n classes,\n}: CTABandProps) {\n const shown: CTABandVariant = (variant === 'photo' && !media) || (variant === 'form' && !form) ? 'plain' : variant\n return (\n <section\n data-bl-section=\"cta\"\n data-variant={shown}\n data-tone={tone ?? (shown === 'photo' ? 'photo' : undefined)}\n data-align={layout?.align ?? 'start'}\n aria-labelledby={headingId}\n className={classes?.root}\n >\n {decoration ? (\n <div data-slot=\"decoration\" aria-hidden=\"true\">\n {decoration}\n </div>\n ) : null}\n {shown === 'photo' ? (\n <div {...slot(classes, 'media')} aria-hidden=\"true\">\n {media}\n </div>\n ) : null}\n <div {...slot(classes, 'inner')}>\n <div {...slot(classes, 'content')}>\n <h2 id={headingId} {...slot(classes, 'heading')}>\n {heading}\n </h2>\n {summary ? <p {...slot(classes, 'summary')}>{summary}</p> : null}\n <div {...slot(classes, 'actions')}>\n <PhoneLink phone={phone} placement=\"cta\" label={phoneLabel} showIcon variant={layout?.phoneVariant ?? 'button'} classes={{ root: classes?.phone }} />\n {shown === 'form' || !quote ? null : (\n <QuoteButton\n label={quote.label}\n href={quote.href}\n placement=\"section\"\n ctaId=\"quote-cta\"\n variant={layout?.quoteVariant ?? 'primary'}\n classes={{ root: classes?.quote }}\n />\n )}\n </div>\n </div>\n {shown === 'form' ? <div {...slot(classes, 'form')}>{form}</div> : null}\n </div>\n </section>\n )\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `FAQs` (S9, `faqs`): real questions and answers from the page's data, on `Accordion` (closed, one open at a time,
|
|
3
|
+
* answers in the server HTML). Never emits `FAQPage` or any other schema (F14). Variants: `list`, or `grouped` for
|
|
4
|
+
* the FAQs page (an H3 per group); one or two columns.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReactNode } from 'react';
|
|
7
|
+
import { type BrandlabSectionReadiness } from '@brandlab-digital/site-core/section-order';
|
|
8
|
+
import { type SectionHeadingSlot, type SectionSlot, type SectionTone } from '../primitives/layout.js';
|
|
9
|
+
import { type SkinEntry } from '../skin.js';
|
|
10
|
+
import { type AccordionSlot } from './accordion.js';
|
|
11
|
+
export declare const FAQS_VARIANTS: readonly ["list", "grouped"];
|
|
12
|
+
export type FAQsVariant = (typeof FAQS_VARIANTS)[number];
|
|
13
|
+
export type FAQsSlot = SectionSlot | SectionHeadingSlot | 'columns' | 'column' | 'group' | 'subheading' | 'accordion' | Exclude<AccordionSlot, 'root'>;
|
|
14
|
+
export type FAQsSkin = SkinEntry<FAQsSlot, FAQsVariant, {
|
|
15
|
+
columns?: 1 | 2;
|
|
16
|
+
}>;
|
|
17
|
+
export interface FAQItem {
|
|
18
|
+
/** A stable slug, unique on the page. */
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly question: string;
|
|
21
|
+
readonly answer: ReactNode;
|
|
22
|
+
/** The group on the FAQs page (`grouped`), for example "Prices and quotes". */
|
|
23
|
+
readonly group?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface FAQsProps extends FAQsSkin {
|
|
26
|
+
/** The H2. */
|
|
27
|
+
readonly title: ReactNode;
|
|
28
|
+
readonly eyebrow?: ReactNode;
|
|
29
|
+
readonly lead?: ReactNode;
|
|
30
|
+
readonly items: readonly FAQItem[];
|
|
31
|
+
/** The question open on load, only where the site recorded one. */
|
|
32
|
+
readonly defaultOpen?: string;
|
|
33
|
+
/** The accordion group name and id prefix. Defaults to `faq`. */
|
|
34
|
+
readonly name?: string;
|
|
35
|
+
/** Defaults to `faqs-heading`. */
|
|
36
|
+
readonly headingId?: string;
|
|
37
|
+
/** Set by `SectionSequence`. */
|
|
38
|
+
readonly tone?: SectionTone;
|
|
39
|
+
}
|
|
40
|
+
/** Whether the section renders: nothing without questions (`no-faqs`). */
|
|
41
|
+
export declare function faqsReady({ items }: Pick<FAQsProps, 'items'>): BrandlabSectionReadiness;
|
|
42
|
+
export declare function FAQs({ title, eyebrow, lead, items, defaultOpen, name, headingId, tone, variant, layout, classes, }: FAQsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
43
|
+
//# sourceMappingURL=faqs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faqs.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/faqs.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,EAAiB,KAAK,wBAAwB,EAAE,MAAM,2CAA2C,CAAA;AACxG,OAAO,EAA2B,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC9H,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE9D,eAAO,MAAM,aAAa,8BAA+B,CAAA;AACzD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AACxD,MAAM,MAAM,QAAQ,GAChB,WAAW,GACX,kBAAkB,GAClB,SAAS,GACT,QAAQ,GACR,OAAO,GACP,YAAY,GACZ,WAAW,GACX,OAAO,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;AAClC,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,EAAE,WAAW,EAAE;IAAE,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;CAAE,CAAC,CAAA;AAE5E,MAAM,WAAW,OAAO;IACtB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAA;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,SAAU,SAAQ,QAAQ;IACzC,cAAc;IACd,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAA;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,CAAA;IAClC,mEAAmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,kCAAkC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,gCAAgC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAA;CAC5B;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,wBAAwB,CAEvF;AAeD,wBAAgB,IAAI,CAAC,EACnB,KAAK,EACL,OAAO,EACP,IAAI,EACJ,KAAK,EACL,WAAW,EACX,IAAY,EACZ,SAA0B,EAC1B,IAAI,EACJ,OAAgB,EAChB,MAAM,EACN,OAAO,GACR,EAAE,SAAS,kDAwCX"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SECTION_READY } from '@brandlab-digital/site-core/section-order';
|
|
3
|
+
import { Section, SectionHeading } from '../primitives/layout.js';
|
|
4
|
+
import { slot } from '../skin.js';
|
|
5
|
+
import { Accordion } from './accordion.js';
|
|
6
|
+
export const FAQS_VARIANTS = ['list', 'grouped'];
|
|
7
|
+
/** Whether the section renders: nothing without questions (`no-faqs`). */
|
|
8
|
+
export function faqsReady({ items }) {
|
|
9
|
+
return items.length > 0 ? SECTION_READY : { render: false, reason: 'no-faqs' };
|
|
10
|
+
}
|
|
11
|
+
/** Questions by group, in first-seen order; questions without a group come under no subheading. */
|
|
12
|
+
function groupItems(items) {
|
|
13
|
+
const groups = new Map();
|
|
14
|
+
for (const item of items)
|
|
15
|
+
groups.set(item.group ?? '', [...(groups.get(item.group ?? '') ?? []), item]);
|
|
16
|
+
return [...groups];
|
|
17
|
+
}
|
|
18
|
+
function halves(list, columns) {
|
|
19
|
+
if (columns === 1 || list.length < 2)
|
|
20
|
+
return [list];
|
|
21
|
+
const split = Math.ceil(list.length / 2);
|
|
22
|
+
return [list.slice(0, split), list.slice(split)];
|
|
23
|
+
}
|
|
24
|
+
export function FAQs({ title, eyebrow, lead, items, defaultOpen, name = 'faq', headingId = 'faqs-heading', tone, variant = 'list', layout, classes, }) {
|
|
25
|
+
if (!faqsReady({ items }).render)
|
|
26
|
+
return null;
|
|
27
|
+
if (new Set(items.map((item) => item.id)).size !== items.length)
|
|
28
|
+
throw new Error('FAQs: question ids must be unique on the page.');
|
|
29
|
+
if (defaultOpen !== undefined && !items.some((item) => item.id === defaultOpen)) {
|
|
30
|
+
throw new Error(`FAQs: defaultOpen "${defaultOpen}" is not a question on this page.`);
|
|
31
|
+
}
|
|
32
|
+
const columns = layout?.columns ?? 1;
|
|
33
|
+
const accordionClasses = { ...classes, root: classes?.accordion };
|
|
34
|
+
const accordion = (list) => (_jsx(Accordion, { name: name, defaultOpen: list.some((item) => item.id === defaultOpen) ? defaultOpen : undefined, items: list.map((item) => ({ id: item.id, summary: item.question, content: item.answer })), classes: accordionClasses }));
|
|
35
|
+
const groups = variant === 'grouped' ? groupItems(items) : null;
|
|
36
|
+
return (_jsxs(Section, { id: "faqs", labelledBy: headingId, tone: tone, variant: variant, classes: classes, children: [_jsx(SectionHeading, { id: headingId, title: title, eyebrow: eyebrow, lead: lead, classes: classes }), _jsx("div", { ...slot(classes, 'columns'), "data-columns": String(columns), children: groups
|
|
37
|
+
? halves(groups, columns).map((column, index) => (_jsx("div", { ...slot(classes, 'column'), children: column.map(([group, list]) => (_jsxs("div", { ...slot(classes, 'group'), children: [group ? _jsx("h3", { ...slot(classes, 'subheading'), children: group }) : null, accordion(list)] }, group))) }, index)))
|
|
38
|
+
: halves(items, columns).map((column, index) => (_jsx("div", { ...slot(classes, 'column'), children: accordion(column) }, index))) })] }));
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=faqs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faqs.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/faqs.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * `FAQs` (S9, `faqs`): real questions and answers from the page's data, on `Accordion` (closed, one open at a time,\n * answers in the server HTML). Never emits `FAQPage` or any other schema (F14). Variants: `list`, or `grouped` for\n * the FAQs page (an H3 per group); one or two columns.\n */\nimport type { ReactNode } from 'react'\nimport { SECTION_READY, type BrandlabSectionReadiness } from '@brandlab-digital/site-core/section-order'\nimport { Section, SectionHeading, type SectionHeadingSlot, type SectionSlot, type SectionTone } from '../primitives/layout.js'\nimport { slot, type SkinEntry } from '../skin.js'\nimport { Accordion, type AccordionSlot } from './accordion.js'\n\nexport const FAQS_VARIANTS = ['list', 'grouped'] as const\nexport type FAQsVariant = (typeof FAQS_VARIANTS)[number]\nexport type FAQsSlot =\n | SectionSlot\n | SectionHeadingSlot\n | 'columns'\n | 'column'\n | 'group'\n | 'subheading'\n | 'accordion'\n | Exclude<AccordionSlot, 'root'>\nexport type FAQsSkin = SkinEntry<FAQsSlot, FAQsVariant, { columns?: 1 | 2 }>\n\nexport interface FAQItem {\n /** A stable slug, unique on the page. */\n readonly id: string\n readonly question: string\n readonly answer: ReactNode\n /** The group on the FAQs page (`grouped`), for example \"Prices and quotes\". */\n readonly group?: string\n}\n\nexport interface FAQsProps extends FAQsSkin {\n /** The H2. */\n readonly title: ReactNode\n readonly eyebrow?: ReactNode\n readonly lead?: ReactNode\n readonly items: readonly FAQItem[]\n /** The question open on load, only where the site recorded one. */\n readonly defaultOpen?: string\n /** The accordion group name and id prefix. Defaults to `faq`. */\n readonly name?: string\n /** Defaults to `faqs-heading`. */\n readonly headingId?: string\n /** Set by `SectionSequence`. */\n readonly tone?: SectionTone\n}\n\n/** Whether the section renders: nothing without questions (`no-faqs`). */\nexport function faqsReady({ items }: Pick<FAQsProps, 'items'>): BrandlabSectionReadiness {\n return items.length > 0 ? SECTION_READY : { render: false, reason: 'no-faqs' }\n}\n\n/** Questions by group, in first-seen order; questions without a group come under no subheading. */\nfunction groupItems(items: readonly FAQItem[]): (readonly [string, readonly FAQItem[]])[] {\n const groups = new Map<string, FAQItem[]>()\n for (const item of items) groups.set(item.group ?? '', [...(groups.get(item.group ?? '') ?? []), item])\n return [...groups]\n}\n\nfunction halves<T>(list: readonly T[], columns: 1 | 2): (readonly T[])[] {\n if (columns === 1 || list.length < 2) return [list]\n const split = Math.ceil(list.length / 2)\n return [list.slice(0, split), list.slice(split)]\n}\n\nexport function FAQs({\n title,\n eyebrow,\n lead,\n items,\n defaultOpen,\n name = 'faq',\n headingId = 'faqs-heading',\n tone,\n variant = 'list',\n layout,\n classes,\n}: FAQsProps) {\n if (!faqsReady({ items }).render) return null\n if (new Set(items.map((item) => item.id)).size !== items.length) throw new Error('FAQs: question ids must be unique on the page.')\n if (defaultOpen !== undefined && !items.some((item) => item.id === defaultOpen)) {\n throw new Error(`FAQs: defaultOpen \"${defaultOpen}\" is not a question on this page.`)\n }\n const columns = layout?.columns ?? 1\n const accordionClasses = { ...classes, root: classes?.accordion }\n const accordion = (list: readonly FAQItem[]) => (\n <Accordion\n name={name}\n defaultOpen={list.some((item) => item.id === defaultOpen) ? defaultOpen : undefined}\n items={list.map((item) => ({ id: item.id, summary: item.question, content: item.answer }))}\n classes={accordionClasses}\n />\n )\n const groups = variant === 'grouped' ? groupItems(items) : null\n return (\n <Section id=\"faqs\" labelledBy={headingId} tone={tone} variant={variant} classes={classes}>\n <SectionHeading id={headingId} title={title} eyebrow={eyebrow} lead={lead} classes={classes} />\n <div {...slot(classes, 'columns')} data-columns={String(columns)}>\n {groups\n ? halves(groups, columns).map((column, index) => (\n <div key={index} {...slot(classes, 'column')}>\n {column.map(([group, list]) => (\n <div key={group} {...slot(classes, 'group')}>\n {group ? <h3 {...slot(classes, 'subheading')}>{group}</h3> : null}\n {accordion(list)}\n </div>\n ))}\n </div>\n ))\n : halves(items, columns).map((column, index) => (\n <div key={index} {...slot(classes, 'column')}>\n {accordion(column)}\n </div>\n ))}\n </div>\n </Section>\n )\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { hasGoogleRating, RatingChip, type RatingChipData, type RatingChipProps, type RatingChipSkin, type RatingChipSlot } from './rating-chip.js';
|
|
2
|
+
export { REVIEWS_BAND_HEADING_ID, REVIEWS_BAND_VARIANTS, ReviewsBand, ReviewsBandHeading, ReviewsBandItem, ReviewsBandList, ReviewsBandRoot, ReviewsBandSummary, splitReviewText, type ReviewsBandHeadingProps, type ReviewsBandItemProps, type ReviewsBandLayout, type ReviewsBandProps, type ReviewsBandRootProps, type ReviewsBandSkin, type ReviewsBandSlot, type ReviewsBandSummaryProps, type ReviewsBandVariant, } from './reviews-band.js';
|
|
3
|
+
export { CTA_BAND_HEADING_ID, CTA_BAND_VARIANTS, CTABand, type CTABandLayout, type CTABandProps, type CTABandSkin, type CTABandSlot, type CTABandVariant } from './cta-band.js';
|
|
4
|
+
export { SITE_FOOTER_VARIANTS, SiteFooter, type SiteFooterColumn, type SiteFooterLicence, type SiteFooterProps, type SiteFooterSkin, type SiteFooterSlot, type SiteFooterVariant, } from './site-footer.js';
|
|
5
|
+
export { Accordion, type AccordionItem, type AccordionProps, type AccordionSkin, type AccordionSlot } from './accordion.js';
|
|
6
|
+
export { FAQS_VARIANTS, FAQs, faqsReady, type FAQItem, type FAQsProps, type FAQsSkin, type FAQsSlot, type FAQsVariant } from './faqs.js';
|
|
7
|
+
export { PriceTable, type PriceLabels, type PriceTableProps, type PriceTableSkin, type PriceTableSlot } from './price-table.js';
|
|
8
|
+
export { PRICE_GUIDE_VARIANTS, PriceGuide, priceGuideReady, type PriceFactor, type PriceGuideProps, type PriceGuideSkin, type PriceGuideSlot, type PriceGuideVariant, } from './price-guide.js';
|
|
9
|
+
export { PROOF_STRIP_MINIMUM, PROOF_STRIP_VARIANTS, ProofStrip, proofStripReady, type ProofFactItem, type ProofItem, type ProofRatingItem, type ProofStripProps, type ProofStripSkin, type ProofStripSlot, type ProofStripVariant, } from './proof-strip.js';
|
|
10
|
+
export { PAGE_HEADER_VARIANTS, PageHeader, type PageHeaderProps, type PageHeaderSkin, type PageHeaderSlot, type PageHeaderVariant } from './page-header.js';
|
|
11
|
+
export { ContactBlock, type ContactBlockProps, type ContactBlockSkin, type ContactBlockSlot, type ContactDetailLines } from './contact-block.js';
|
|
12
|
+
export { SectionSequence, serialiseSectionManifest, type SectionSequenceProps, type SequenceSection } from './section-sequence.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACnJ,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAA;AAC/K,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC3H,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AACxI,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAC/H,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAC3J,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAChJ,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,KAAK,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { hasGoogleRating, RatingChip } from './rating-chip.js';
|
|
2
|
+
export { REVIEWS_BAND_HEADING_ID, REVIEWS_BAND_VARIANTS, ReviewsBand, ReviewsBandHeading, ReviewsBandItem, ReviewsBandList, ReviewsBandRoot, ReviewsBandSummary, splitReviewText, } from './reviews-band.js';
|
|
3
|
+
export { CTA_BAND_HEADING_ID, CTA_BAND_VARIANTS, CTABand } from './cta-band.js';
|
|
4
|
+
export { SITE_FOOTER_VARIANTS, SiteFooter, } from './site-footer.js';
|
|
5
|
+
export { Accordion } from './accordion.js';
|
|
6
|
+
export { FAQS_VARIANTS, FAQs, faqsReady } from './faqs.js';
|
|
7
|
+
export { PriceTable } from './price-table.js';
|
|
8
|
+
export { PRICE_GUIDE_VARIANTS, PriceGuide, priceGuideReady, } from './price-guide.js';
|
|
9
|
+
export { PROOF_STRIP_MINIMUM, PROOF_STRIP_VARIANTS, ProofStrip, proofStripReady, } from './proof-strip.js';
|
|
10
|
+
export { PAGE_HEADER_VARIANTS, PageHeader } from './page-header.js';
|
|
11
|
+
export { ContactBlock } from './contact-block.js';
|
|
12
|
+
export { SectionSequence, serialiseSectionManifest } from './section-sequence.js';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAuF,MAAM,kBAAkB,CAAA;AACnJ,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,eAAe,GAUhB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,OAAO,EAAkG,MAAM,eAAe,CAAA;AAC/K,OAAO,EACL,oBAAoB,EACpB,UAAU,GAOX,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,SAAS,EAAmF,MAAM,gBAAgB,CAAA;AAC3H,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAgF,MAAM,WAAW,CAAA;AACxI,OAAO,EAAE,UAAU,EAAoF,MAAM,kBAAkB,CAAA;AAC/H,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,GAMhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,eAAe,GAQhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAA0F,MAAM,kBAAkB,CAAA;AAC3J,OAAO,EAAE,YAAY,EAAiG,MAAM,oBAAoB,CAAA;AAChJ,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAmD,MAAM,uBAAuB,CAAA","sourcesContent":["export { hasGoogleRating, RatingChip, type RatingChipData, type RatingChipProps, type RatingChipSkin, type RatingChipSlot } from './rating-chip.js'\nexport {\n REVIEWS_BAND_HEADING_ID,\n REVIEWS_BAND_VARIANTS,\n ReviewsBand,\n ReviewsBandHeading,\n ReviewsBandItem,\n ReviewsBandList,\n ReviewsBandRoot,\n ReviewsBandSummary,\n splitReviewText,\n type ReviewsBandHeadingProps,\n type ReviewsBandItemProps,\n type ReviewsBandLayout,\n type ReviewsBandProps,\n type ReviewsBandRootProps,\n type ReviewsBandSkin,\n type ReviewsBandSlot,\n type ReviewsBandSummaryProps,\n type ReviewsBandVariant,\n} from './reviews-band.js'\nexport { CTA_BAND_HEADING_ID, CTA_BAND_VARIANTS, CTABand, type CTABandLayout, type CTABandProps, type CTABandSkin, type CTABandSlot, type CTABandVariant } from './cta-band.js'\nexport {\n SITE_FOOTER_VARIANTS,\n SiteFooter,\n type SiteFooterColumn,\n type SiteFooterLicence,\n type SiteFooterProps,\n type SiteFooterSkin,\n type SiteFooterSlot,\n type SiteFooterVariant,\n} from './site-footer.js'\nexport { Accordion, type AccordionItem, type AccordionProps, type AccordionSkin, type AccordionSlot } from './accordion.js'\nexport { FAQS_VARIANTS, FAQs, faqsReady, type FAQItem, type FAQsProps, type FAQsSkin, type FAQsSlot, type FAQsVariant } from './faqs.js'\nexport { PriceTable, type PriceLabels, type PriceTableProps, type PriceTableSkin, type PriceTableSlot } from './price-table.js'\nexport {\n PRICE_GUIDE_VARIANTS,\n PriceGuide,\n priceGuideReady,\n type PriceFactor,\n type PriceGuideProps,\n type PriceGuideSkin,\n type PriceGuideSlot,\n type PriceGuideVariant,\n} from './price-guide.js'\nexport {\n PROOF_STRIP_MINIMUM,\n PROOF_STRIP_VARIANTS,\n ProofStrip,\n proofStripReady,\n type ProofFactItem,\n type ProofItem,\n type ProofRatingItem,\n type ProofStripProps,\n type ProofStripSkin,\n type ProofStripSlot,\n type ProofStripVariant,\n} from './proof-strip.js'\nexport { PAGE_HEADER_VARIANTS, PageHeader, type PageHeaderProps, type PageHeaderSkin, type PageHeaderSlot, type PageHeaderVariant } from './page-header.js'\nexport { ContactBlock, type ContactBlockProps, type ContactBlockSkin, type ContactBlockSlot, type ContactDetailLines } from './contact-block.js'\nexport { SectionSequence, serialiseSectionManifest, type SectionSequenceProps, type SequenceSection } from './section-sequence.js'\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `PageHeader` (S2, `page-header`): the simple header of every hub and company page, contact included. Server
|
|
3
|
+
* component: the page's one H1, an optional one-line summary and the breadcrumbs. No form, no phone button and no
|
|
4
|
+
* review quote (Romie, ATS, Prestige and AB), so it takes no other content. Variants: `plain`, or `photo` (the site's
|
|
5
|
+
* image behind, under a scrim the site colours, as the hero's scrim rule).
|
|
6
|
+
*/
|
|
7
|
+
import type { ReactNode } from 'react';
|
|
8
|
+
import { type SectionTone } from '../primitives/layout.js';
|
|
9
|
+
import { type SkinEntry } from '../skin.js';
|
|
10
|
+
export declare const PAGE_HEADER_VARIANTS: readonly ["plain", "photo"];
|
|
11
|
+
export type PageHeaderVariant = (typeof PAGE_HEADER_VARIANTS)[number];
|
|
12
|
+
export type PageHeaderSlot = 'root' | 'inner' | 'crumbs' | 'h1' | 'summary' | 'media' | 'scrim';
|
|
13
|
+
export type PageHeaderSkin = SkinEntry<PageHeaderSlot, PageHeaderVariant>;
|
|
14
|
+
export interface PageHeaderProps extends PageHeaderSkin {
|
|
15
|
+
/** The page's H1, rendered once. */
|
|
16
|
+
readonly title: ReactNode;
|
|
17
|
+
/** One line under the H1. */
|
|
18
|
+
readonly summary?: ReactNode;
|
|
19
|
+
/** The page's breadcrumbs (the shared `Breadcrumbs`, with its own `nav`). */
|
|
20
|
+
readonly breadcrumbs?: ReactNode;
|
|
21
|
+
/** The `photo` variant's image (the site's `Media`). Ignored by `plain`. */
|
|
22
|
+
readonly media?: ReactNode;
|
|
23
|
+
/** Defaults to `page-title`. */
|
|
24
|
+
readonly headingId?: string;
|
|
25
|
+
/** Set by `SectionSequence`. A page using the `photo` variant offers its header with `tone: 'photo'`. */
|
|
26
|
+
readonly tone?: SectionTone;
|
|
27
|
+
}
|
|
28
|
+
export declare function PageHeader({ title, summary, breadcrumbs, media, headingId, tone, variant, classes }: PageHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
//# sourceMappingURL=page-header.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-header.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/page-header.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACnE,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AAEjD,eAAO,MAAM,oBAAoB,6BAA8B,CAAA;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AACrE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;AAC/F,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC,cAAc,EAAE,iBAAiB,CAAC,CAAA;AAEzE,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,oCAAoC;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,6BAA6B;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAA;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,CAAA;IAChC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAA;IAC1B,gCAAgC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,yGAAyG;IACzG,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAA;CAC5B;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,SAAwB,EAAE,IAAI,EAAE,OAAiB,EAAE,OAAO,EAAE,EAAE,eAAe,2CAiB7I"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Section } from '../primitives/layout.js';
|
|
3
|
+
import { slot } from '../skin.js';
|
|
4
|
+
export const PAGE_HEADER_VARIANTS = ['plain', 'photo'];
|
|
5
|
+
export function PageHeader({ title, summary, breadcrumbs, media, headingId = 'page-title', tone, variant = 'plain', classes }) {
|
|
6
|
+
const photo = variant === 'photo' && media ? media : null;
|
|
7
|
+
return (_jsxs(Section, { id: "page-header", labelledBy: headingId, tone: tone, variant: variant, classes: classes, children: [photo ? (_jsxs(_Fragment, { children: [_jsx("div", { ...slot(classes, 'media'), children: photo }), _jsx("div", { ...slot(classes, 'scrim'), "aria-hidden": "true" })] })) : null, breadcrumbs ? _jsx("div", { ...slot(classes, 'crumbs'), children: breadcrumbs }) : null, _jsx("h1", { id: headingId, ...slot(classes, 'h1'), children: title }), summary ? _jsx("p", { ...slot(classes, 'summary'), children: summary }) : null] }));
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=page-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-header.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/page-header.tsx"],"names":[],"mappings":";AAOA,OAAO,EAAE,OAAO,EAAoB,MAAM,yBAAyB,CAAA;AACnE,OAAO,EAAE,IAAI,EAAkB,MAAM,YAAY,CAAA;AAEjD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE,OAAO,CAAU,CAAA;AAoB/D,MAAM,UAAU,UAAU,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,GAAG,YAAY,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,EAAmB;IAC5I,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;IACzD,OAAO,CACL,MAAC,OAAO,IAAC,EAAE,EAAC,aAAa,EAAC,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,aAC5F,KAAK,CAAC,CAAC,CAAC,CACP,8BACE,iBAAS,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,YAAG,KAAK,GAAO,EAC9C,iBAAS,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,iBAAc,MAAM,GAAG,IACrD,CACJ,CAAC,CAAC,CAAC,IAAI,EACP,WAAW,CAAC,CAAC,CAAC,iBAAS,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAG,WAAW,GAAO,CAAC,CAAC,CAAC,IAAI,EAC3E,aAAI,EAAE,EAAE,SAAS,KAAM,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,YACvC,KAAK,GACH,EACJ,OAAO,CAAC,CAAC,CAAC,eAAO,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,YAAG,OAAO,GAAK,CAAC,CAAC,CAAC,IAAI,IACxD,CACX,CAAA;AACH,CAAC","sourcesContent":["/**\n * `PageHeader` (S2, `page-header`): the simple header of every hub and company page, contact included. Server\n * component: the page's one H1, an optional one-line summary and the breadcrumbs. No form, no phone button and no\n * review quote (Romie, ATS, Prestige and AB), so it takes no other content. Variants: `plain`, or `photo` (the site's\n * image behind, under a scrim the site colours, as the hero's scrim rule).\n */\nimport type { ReactNode } from 'react'\nimport { Section, type SectionTone } from '../primitives/layout.js'\nimport { slot, type SkinEntry } from '../skin.js'\n\nexport const PAGE_HEADER_VARIANTS = ['plain', 'photo'] as const\nexport type PageHeaderVariant = (typeof PAGE_HEADER_VARIANTS)[number]\nexport type PageHeaderSlot = 'root' | 'inner' | 'crumbs' | 'h1' | 'summary' | 'media' | 'scrim'\nexport type PageHeaderSkin = SkinEntry<PageHeaderSlot, PageHeaderVariant>\n\nexport interface PageHeaderProps extends PageHeaderSkin {\n /** The page's H1, rendered once. */\n readonly title: ReactNode\n /** One line under the H1. */\n readonly summary?: ReactNode\n /** The page's breadcrumbs (the shared `Breadcrumbs`, with its own `nav`). */\n readonly breadcrumbs?: ReactNode\n /** The `photo` variant's image (the site's `Media`). Ignored by `plain`. */\n readonly media?: ReactNode\n /** Defaults to `page-title`. */\n readonly headingId?: string\n /** Set by `SectionSequence`. A page using the `photo` variant offers its header with `tone: 'photo'`. */\n readonly tone?: SectionTone\n}\n\nexport function PageHeader({ title, summary, breadcrumbs, media, headingId = 'page-title', tone, variant = 'plain', classes }: PageHeaderProps) {\n const photo = variant === 'photo' && media ? media : null\n return (\n <Section id=\"page-header\" labelledBy={headingId} tone={tone} variant={variant} classes={classes}>\n {photo ? (\n <>\n <div {...slot(classes, 'media')}>{photo}</div>\n <div {...slot(classes, 'scrim')} aria-hidden=\"true\" />\n </>\n ) : null}\n {breadcrumbs ? <div {...slot(classes, 'crumbs')}>{breadcrumbs}</div> : null}\n <h1 id={headingId} {...slot(classes, 'h1')}>\n {title}\n </h1>\n {summary ? <p {...slot(classes, 'summary')}>{summary}</p> : null}\n </Section>\n )\n}\n"]}
|