@brandlab-digital/site-next 3.12.1 → 3.14.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 +132 -0
- package/dist/contact/delivery.d.ts +81 -0
- package/dist/contact/delivery.d.ts.map +1 -0
- package/dist/contact/delivery.js +138 -0
- package/dist/contact/delivery.js.map +1 -0
- package/dist/contact/resend-webhook.d.ts +72 -0
- package/dist/contact/resend-webhook.d.ts.map +1 -0
- package/dist/contact/resend-webhook.js +300 -0
- package/dist/contact/resend-webhook.js.map +1 -0
- package/dist/contact/route-handler.d.ts +16 -0
- package/dist/contact/route-handler.d.ts.map +1 -1
- package/dist/contact/route-handler.js +8 -0
- package/dist/contact/route-handler.js.map +1 -1
- package/dist/contact/server-entry.d.ts +2 -0
- package/dist/contact/server-entry.d.ts.map +1 -1
- package/dist/contact/server-entry.js +2 -0
- package/dist/contact/server-entry.js.map +1 -1
- package/dist/contact/server.d.ts +26 -0
- package/dist/contact/server.d.ts.map +1 -1
- package/dist/contact/server.js +48 -3
- package/dist/contact/server.js.map +1 -1
- package/dist/estate-segment.d.ts +48 -0
- package/dist/estate-segment.d.ts.map +1 -0
- package/dist/estate-segment.js +54 -0
- package/dist/estate-segment.js.map +1 -0
- package/dist/google-business-reviews-revalidate.d.ts +61 -0
- package/dist/google-business-reviews-revalidate.d.ts.map +1 -0
- package/dist/google-business-reviews-revalidate.js +151 -0
- package/dist/google-business-reviews-revalidate.js.map +1 -0
- package/dist/google-business-reviews-server.d.ts +21 -2
- package/dist/google-business-reviews-server.d.ts.map +1 -1
- package/dist/google-business-reviews-server.js +21 -4
- package/dist/google-business-reviews-server.js.map +1 -1
- package/dist/headers.d.ts.map +1 -1
- package/dist/headers.js +6 -0
- package/dist/headers.js.map +1 -1
- package/dist/next-config.d.ts +17 -1
- package/dist/next-config.d.ts.map +1 -1
- package/dist/next-config.js +36 -2
- package/dist/next-config.js.map +1 -1
- package/dist/robots.d.ts +26 -4
- package/dist/robots.d.ts.map +1 -1
- package/dist/robots.js +49 -2
- package/dist/robots.js.map +1 -1
- package/dist/ui-kit/brandlab-skin.d.ts +24 -1
- 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/form/photo-upload.d.ts +4 -1
- package/dist/ui-kit/form/photo-upload.d.ts.map +1 -1
- package/dist/ui-kit/form/photo-upload.js +4 -4
- package/dist/ui-kit/form/photo-upload.js.map +1 -1
- package/dist/ui-kit/form/quote-form.d.ts +4 -1
- package/dist/ui-kit/form/quote-form.d.ts.map +1 -1
- package/dist/ui-kit/form/quote-form.js.map +1 -1
- package/dist/ui-kit/form/thank-you.d.ts +4 -1
- package/dist/ui-kit/form/thank-you.d.ts.map +1 -1
- package/dist/ui-kit/form/thank-you.js +2 -2
- package/dist/ui-kit/form/thank-you.js.map +1 -1
- package/dist/ui-kit/icon-glyphs.d.ts +41 -0
- package/dist/ui-kit/icon-glyphs.d.ts.map +1 -0
- package/dist/ui-kit/icon-glyphs.js +138 -0
- package/dist/ui-kit/icon-glyphs.js.map +1 -0
- package/dist/ui-kit/icon-sprite.d.ts +19 -0
- package/dist/ui-kit/icon-sprite.d.ts.map +1 -0
- package/dist/ui-kit/icon-sprite.js +44 -0
- package/dist/ui-kit/icon-sprite.js.map +1 -0
- package/dist/ui-kit/icons.d.ts +38 -3
- package/dist/ui-kit/icons.d.ts.map +1 -1
- package/dist/ui-kit/icons.js +47 -64
- package/dist/ui-kit/icons.js.map +1 -1
- package/dist/ui-kit/prefetch.d.ts +50 -0
- package/dist/ui-kit/prefetch.d.ts.map +1 -0
- package/dist/ui-kit/prefetch.js +50 -0
- package/dist/ui-kit/prefetch.js.map +1 -0
- package/dist/ui-kit/primitives/badge.d.ts +25 -0
- package/dist/ui-kit/primitives/badge.d.ts.map +1 -0
- package/dist/ui-kit/primitives/badge.js +21 -0
- package/dist/ui-kit/primitives/badge.js.map +1 -0
- package/dist/ui-kit/primitives/breadcrumbs.d.ts +27 -0
- package/dist/ui-kit/primitives/breadcrumbs.d.ts.map +1 -0
- package/dist/ui-kit/primitives/breadcrumbs.js +40 -0
- package/dist/ui-kit/primitives/breadcrumbs.js.map +1 -0
- package/dist/ui-kit/primitives/button.d.ts +32 -0
- package/dist/ui-kit/primitives/button.d.ts.map +1 -0
- package/dist/ui-kit/primitives/button.js +8 -0
- package/dist/ui-kit/primitives/button.js.map +1 -0
- package/dist/ui-kit/primitives/card.d.ts +40 -0
- package/dist/ui-kit/primitives/card.d.ts.map +1 -0
- package/dist/ui-kit/primitives/card.js +8 -0
- package/dist/ui-kit/primitives/card.js.map +1 -0
- package/dist/ui-kit/primitives/index.d.ts +12 -2
- package/dist/ui-kit/primitives/index.d.ts.map +1 -1
- package/dist/ui-kit/primitives/index.js +12 -2
- package/dist/ui-kit/primitives/index.js.map +1 -1
- package/dist/ui-kit/primitives/layout.d.ts +12 -0
- package/dist/ui-kit/primitives/layout.d.ts.map +1 -1
- package/dist/ui-kit/primitives/layout.js +20 -2
- package/dist/ui-kit/primitives/layout.js.map +1 -1
- package/dist/ui-kit/primitives/links.d.ts +28 -2
- package/dist/ui-kit/primitives/links.d.ts.map +1 -1
- package/dist/ui-kit/primitives/links.js +11 -6
- package/dist/ui-kit/primitives/links.js.map +1 -1
- package/dist/ui-kit/primitives/lists.d.ts +43 -0
- package/dist/ui-kit/primitives/lists.d.ts.map +1 -0
- package/dist/ui-kit/primitives/lists.js +34 -0
- package/dist/ui-kit/primitives/lists.js.map +1 -0
- package/dist/ui-kit/primitives/media-frame.d.ts +28 -0
- package/dist/ui-kit/primitives/media-frame.d.ts.map +1 -0
- package/dist/ui-kit/primitives/media-frame.js +58 -0
- package/dist/ui-kit/primitives/media-frame.js.map +1 -0
- package/dist/ui-kit/primitives/media.d.ts +38 -0
- package/dist/ui-kit/primitives/media.d.ts.map +1 -0
- package/dist/ui-kit/primitives/media.js +9 -0
- package/dist/ui-kit/primitives/media.js.map +1 -0
- package/dist/ui-kit/primitives/quote-button.d.ts +4 -1
- package/dist/ui-kit/primitives/quote-button.d.ts.map +1 -1
- package/dist/ui-kit/primitives/quote-button.js +3 -2
- package/dist/ui-kit/primitives/quote-button.js.map +1 -1
- package/dist/ui-kit/primitives/stat.d.ts +25 -0
- package/dist/ui-kit/primitives/stat.d.ts.map +1 -0
- package/dist/ui-kit/primitives/stat.js +7 -0
- package/dist/ui-kit/primitives/stat.js.map +1 -0
- package/dist/ui-kit/repeated-slots.d.ts +121 -0
- package/dist/ui-kit/repeated-slots.d.ts.map +1 -0
- package/dist/ui-kit/repeated-slots.js +81 -0
- package/dist/ui-kit/repeated-slots.js.map +1 -0
- package/dist/ui-kit/sections/accordion.d.ts +4 -1
- package/dist/ui-kit/sections/accordion.d.ts.map +1 -1
- package/dist/ui-kit/sections/accordion.js +2 -2
- package/dist/ui-kit/sections/accordion.js.map +1 -1
- package/dist/ui-kit/sections/contact-block.d.ts +4 -1
- package/dist/ui-kit/sections/contact-block.d.ts.map +1 -1
- package/dist/ui-kit/sections/contact-block.js +3 -3
- package/dist/ui-kit/sections/contact-block.js.map +1 -1
- package/dist/ui-kit/sections/faqs.d.ts +4 -1
- package/dist/ui-kit/sections/faqs.d.ts.map +1 -1
- package/dist/ui-kit/sections/faqs.js +4 -5
- package/dist/ui-kit/sections/faqs.js.map +1 -1
- package/dist/ui-kit/sections/hero.d.ts +113 -0
- package/dist/ui-kit/sections/hero.d.ts.map +1 -0
- package/dist/ui-kit/sections/hero.js +58 -0
- package/dist/ui-kit/sections/hero.js.map +1 -0
- package/dist/ui-kit/sections/index.d.ts +2 -0
- package/dist/ui-kit/sections/index.d.ts.map +1 -1
- package/dist/ui-kit/sections/index.js +2 -0
- package/dist/ui-kit/sections/index.js.map +1 -1
- package/dist/ui-kit/sections/price-guide.d.ts +4 -1
- package/dist/ui-kit/sections/price-guide.d.ts.map +1 -1
- package/dist/ui-kit/sections/price-guide.js +3 -3
- package/dist/ui-kit/sections/price-guide.js.map +1 -1
- package/dist/ui-kit/sections/price-table.d.ts +7 -9
- package/dist/ui-kit/sections/price-table.d.ts.map +1 -1
- package/dist/ui-kit/sections/price-table.js +8 -8
- package/dist/ui-kit/sections/price-table.js.map +1 -1
- package/dist/ui-kit/sections/proof-strip.d.ts +4 -1
- package/dist/ui-kit/sections/proof-strip.d.ts.map +1 -1
- package/dist/ui-kit/sections/proof-strip.js +2 -2
- package/dist/ui-kit/sections/proof-strip.js.map +1 -1
- package/dist/ui-kit/sections/reviews-band.d.ts +5 -2
- package/dist/ui-kit/sections/reviews-band.d.ts.map +1 -1
- package/dist/ui-kit/sections/reviews-band.js +2 -2
- package/dist/ui-kit/sections/reviews-band.js.map +1 -1
- package/dist/ui-kit/sections/services-grid.d.ts +82 -0
- package/dist/ui-kit/sections/services-grid.d.ts.map +1 -0
- package/dist/ui-kit/sections/services-grid.js +90 -0
- package/dist/ui-kit/sections/services-grid.js.map +1 -0
- package/dist/ui-kit/sections/site-footer.d.ts +4 -1
- package/dist/ui-kit/sections/site-footer.d.ts.map +1 -1
- package/dist/ui-kit/sections/site-footer.js +9 -6
- package/dist/ui-kit/sections/site-footer.js.map +1 -1
- package/dist/ui-kit/shell/current-link.d.ts +15 -1
- package/dist/ui-kit/shell/current-link.d.ts.map +1 -1
- package/dist/ui-kit/shell/current-link.js +10 -1
- package/dist/ui-kit/shell/current-link.js.map +1 -1
- package/dist/ui-kit/shell/index.d.ts +1 -0
- package/dist/ui-kit/shell/index.d.ts.map +1 -1
- package/dist/ui-kit/shell/index.js +1 -0
- package/dist/ui-kit/shell/index.js.map +1 -1
- package/dist/ui-kit/shell/intent-prefetch.d.ts +4 -0
- package/dist/ui-kit/shell/intent-prefetch.d.ts.map +1 -0
- package/dist/ui-kit/shell/intent-prefetch.js +61 -0
- package/dist/ui-kit/shell/intent-prefetch.js.map +1 -0
- package/dist/ui-kit/shell/mobile-drawer.d.ts +7 -3
- package/dist/ui-kit/shell/mobile-drawer.d.ts.map +1 -1
- package/dist/ui-kit/shell/mobile-drawer.js +20 -4
- package/dist/ui-kit/shell/mobile-drawer.js.map +1 -1
- package/dist/ui-kit/shell/site-header.d.ts +4 -1
- package/dist/ui-kit/shell/site-header.d.ts.map +1 -1
- package/dist/ui-kit/shell/site-header.js +9 -10
- package/dist/ui-kit/shell/site-header.js.map +1 -1
- package/dist/ui-kit/shell/social-links.d.ts +1 -2
- package/dist/ui-kit/shell/social-links.d.ts.map +1 -1
- package/dist/ui-kit/shell/social-links.js +6 -3
- package/dist/ui-kit/shell/social-links.js.map +1 -1
- package/dist/ui-kit/shell/utility-bar.d.ts +4 -1
- package/dist/ui-kit/shell/utility-bar.d.ts.map +1 -1
- package/dist/ui-kit/shell/utility-bar.js +2 -2
- package/dist/ui-kit/shell/utility-bar.js.map +1 -1
- package/dist/ui-kit/skin-index.d.ts +2 -1
- package/dist/ui-kit/skin-index.d.ts.map +1 -1
- package/dist/ui-kit/skin-index.js +2 -1
- package/dist/ui-kit/skin-index.js.map +1 -1
- package/dist/ui-kit/skin.d.ts +21 -4
- package/dist/ui-kit/skin.d.ts.map +1 -1
- package/dist/ui-kit/skin.js +14 -1
- package/dist/ui-kit/skin.js.map +1 -1
- package/dist/ui-kit/structure.css +383 -3
- package/package.json +10 -2
|
@@ -7,13 +7,16 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { ReactNode } from 'react';
|
|
9
9
|
import { type SectionTone } from '../primitives/layout.js';
|
|
10
|
+
import type { RepeatedSlotOf } from '../repeated-slots.js';
|
|
10
11
|
import { type SkinEntry } from '../skin.js';
|
|
11
12
|
import type { UiLink, UiPhone } from '../types.js';
|
|
12
13
|
/** `phone` and `link` are the classes for the nested `PhoneLink` and the email and areas links. */
|
|
13
14
|
export type ContactBlockSlot = 'root' | 'inner' | 'form' | 'details' | 'heading' | 'list' | 'item' | 'label' | 'value' | 'line' | 'phone' | 'link';
|
|
15
|
+
/** Rendered once per line of hours, address, licences or areas: styled from the skin stylesheet, never by class (3.14.0). */
|
|
16
|
+
export type ContactBlockRepeatedSlot = RepeatedSlotOf<'contactBlock'>;
|
|
14
17
|
export type ContactBlockSkin = SkinEntry<ContactBlockSlot, never, {
|
|
15
18
|
formSide?: 'start' | 'end';
|
|
16
|
-
}>;
|
|
19
|
+
}, ContactBlockRepeatedSlot>;
|
|
17
20
|
/** A labelled detail of one or more lines, for example hours or a licence. */
|
|
18
21
|
export interface ContactDetailLines {
|
|
19
22
|
readonly label: string;
|
|
@@ -1 +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,
|
|
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,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AAC/D,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,6HAA6H;AAC7H,MAAM,MAAM,wBAAwB,GAAG,cAAc,CAAC,cAAc,CAAC,CAAA;AACrE,MAAM,MAAM,gBAAgB,GAAG,SAAS,CAAC,gBAAgB,EAAE,KAAK,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;CAAE,EAAE,wBAAwB,CAAC,CAAA;AAE3H,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"}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Section } from '../primitives/layout.js';
|
|
3
3
|
import { PhoneLink, TextLink } from '../primitives/links.js';
|
|
4
|
-
import { slot } from '../skin.js';
|
|
4
|
+
import { repeatedSlot, slot } from '../skin.js';
|
|
5
5
|
const EMAIL = /^[^\s@<>()]+@[^\s@<>()]+\.[^\s@<>()]+$/;
|
|
6
6
|
export function ContactBlock({ title, form, phone, email, hours, address, licences, areas, headingId = 'contact-heading', tone, layout, classes, }) {
|
|
7
7
|
if (email && !EMAIL.test(email.address))
|
|
8
8
|
throw new Error(`ContactBlock: "${email.address}" is not an email address.`);
|
|
9
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", { ...
|
|
10
|
+
const lines = (detail) => detail.lines.map((line) => (_jsx("span", { ...repeatedSlot('line'), children: line }, line)));
|
|
11
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", { ...
|
|
12
|
+
? item('areas', areas.label, _jsxs(_Fragment, { children: [_jsx("span", { ...repeatedSlot('line'), children: areas.text }), areas.link ? (_jsx("span", { ...repeatedSlot('line'), children: _jsx(TextLink, { href: areas.link.href, classes: { root: classes?.link }, children: areas.link.label }) })) : null] }))
|
|
13
13
|
: null] })) : null] })] }));
|
|
14
14
|
}
|
|
15
15
|
//# sourceMappingURL=contact-block.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-block.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/contact-block.tsx"],"names":[],"mappings":";AAQA,OAAO,EAAE,OAAO,EAAoB,MAAM,yBAAyB,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;
|
|
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 type { RepeatedSlotOf } from '../repeated-slots.js'\nimport { repeatedSlot, 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'\n/** Rendered once per line of hours, address, licences or areas: styled from the skin stylesheet, never by class (3.14.0). */\nexport type ContactBlockRepeatedSlot = RepeatedSlotOf<'contactBlock'>\nexport type ContactBlockSkin = SkinEntry<ContactBlockSlot, never, { formSide?: 'start' | 'end' }, ContactBlockRepeatedSlot>\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} {...repeatedSlot('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 {...repeatedSlot('line')}>{areas.text}</span>\n {areas.link ? (\n <span {...repeatedSlot('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"]}
|
|
@@ -6,14 +6,17 @@
|
|
|
6
6
|
import type { ReactNode } from 'react';
|
|
7
7
|
import { type BrandlabSectionReadiness } from '@brandlab-digital/site-core/section-order';
|
|
8
8
|
import { type SectionHeadingSlot, type SectionSlot, type SectionTone } from '../primitives/layout.js';
|
|
9
|
+
import type { RepeatedSlotOf } from '../repeated-slots.js';
|
|
9
10
|
import { type SkinEntry } from '../skin.js';
|
|
10
11
|
import { type AccordionSlot } from './accordion.js';
|
|
11
12
|
export declare const FAQS_VARIANTS: readonly ["list", "grouped"];
|
|
12
13
|
export type FAQsVariant = (typeof FAQS_VARIANTS)[number];
|
|
13
14
|
export type FAQsSlot = SectionSlot | SectionHeadingSlot | 'columns' | 'column' | 'group' | 'subheading' | 'accordion' | Exclude<AccordionSlot, 'root'>;
|
|
15
|
+
/** Rendered once per column, group or question: styled from the skin stylesheet, never by class (3.14.0). */
|
|
16
|
+
export type FAQsRepeatedSlot = RepeatedSlotOf<'faqs'>;
|
|
14
17
|
export type FAQsSkin = SkinEntry<FAQsSlot, FAQsVariant, {
|
|
15
18
|
columns?: 1 | 2;
|
|
16
|
-
}>;
|
|
19
|
+
}, FAQsRepeatedSlot>;
|
|
17
20
|
export interface FAQItem {
|
|
18
21
|
/** A stable slug, unique on the page. */
|
|
19
22
|
readonly id: string;
|
|
@@ -1 +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,
|
|
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,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AAC/D,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,6GAA6G;AAC7G,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,EAAE,WAAW,EAAE;IAAE,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;CAAE,EAAE,gBAAgB,CAAC,CAAA;AAE9F,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,kDAsCX"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { SECTION_READY } from '@brandlab-digital/site-core/section-order';
|
|
3
3
|
import { Section, SectionHeading } from '../primitives/layout.js';
|
|
4
|
-
import { slot } from '../skin.js';
|
|
4
|
+
import { repeatedSlot, slot } from '../skin.js';
|
|
5
5
|
import { Accordion } from './accordion.js';
|
|
6
6
|
export const FAQS_VARIANTS = ['list', 'grouped'];
|
|
7
7
|
/** Whether the section renders: nothing without questions (`no-faqs`). */
|
|
@@ -30,11 +30,10 @@ export function FAQs({ title, eyebrow, lead, items, defaultOpen, name = 'faq', h
|
|
|
30
30
|
throw new Error(`FAQs: defaultOpen "${defaultOpen}" is not a question on this page.`);
|
|
31
31
|
}
|
|
32
32
|
const columns = layout?.columns ?? 1;
|
|
33
|
-
const
|
|
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 }));
|
|
33
|
+
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 })) }));
|
|
35
34
|
const groups = variant === 'grouped' ? groupItems(items) : null;
|
|
36
35
|
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", { ...
|
|
38
|
-
: halves(items, columns).map((column, index) => (_jsx("div", { ...
|
|
36
|
+
? halves(groups, columns).map((column, index) => (_jsx("div", { ...repeatedSlot('column'), children: column.map(([group, list]) => (_jsxs("div", { ...repeatedSlot('group'), children: [group ? _jsx("h3", { ...repeatedSlot('subheading'), children: group }) : null, accordion(list)] }, group))) }, index)))
|
|
37
|
+
: halves(items, columns).map((column, index) => (_jsx("div", { ...repeatedSlot('column'), children: accordion(column) }, index))) })] }));
|
|
39
38
|
}
|
|
40
39
|
//# sourceMappingURL=faqs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faqs.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/faqs.tsx"],"names":[],"mappings":";AAMA,OAAO,EAAE,aAAa,EAAiC,MAAM,2CAA2C,CAAA;AACxG,OAAO,EAAE,OAAO,EAAE,cAAc,EAA+D,MAAM,yBAAyB,CAAA;
|
|
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 type { RepeatedSlotOf } from '../repeated-slots.js'\nimport { repeatedSlot, 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'>\n/** Rendered once per column, group or question: styled from the skin stylesheet, never by class (3.14.0). */\nexport type FAQsRepeatedSlot = RepeatedSlotOf<'faqs'>\nexport type FAQsSkin = SkinEntry<FAQsSlot, FAQsVariant, { columns?: 1 | 2 }, FAQsRepeatedSlot>\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 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 />\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} {...repeatedSlot('column')}>\n {column.map(([group, list]) => (\n <div key={group} {...repeatedSlot('group')}>\n {group ? <h3 {...repeatedSlot('subheading')}>{group}</h3> : null}\n {accordion(list)}\n </div>\n ))}\n </div>\n ))\n : halves(items, columns).map((column, index) => (\n <div key={index} {...repeatedSlot('column')}>\n {accordion(column)}\n </div>\n ))}\n </div>\n </Section>\n )\n}\n"]}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Hero` (S1, `hero`, 3.14.0): the first screen of the home, service and service-in-suburb pages. Server component;
|
|
3
|
+
* it wins the click and starts the enquiry. Light by default: every colour comes from the site's tokens and skin.
|
|
4
|
+
*
|
|
5
|
+
* In source order, which is the phone order: breadcrumbs (service and location pages), an optional eyebrow, the H1
|
|
6
|
+
* (once, as plain text; on a location page the built "{Service} {Suburb}" string), one offer line of 25 words or fewer,
|
|
7
|
+
* the two actions, the trust checklist (up to six verified facts, two columns), Google's feed rating, partner badges,
|
|
8
|
+
* the photo and then the site's quote form. On desktop the form sits beside the text (`split`, the photo on the other side) or over the photo
|
|
9
|
+
* as a card (`full-bleed`, under a text-side scrim the skin colours).
|
|
10
|
+
*
|
|
11
|
+
* Locked:
|
|
12
|
+
* - the actions are the phone link (`data-phone-location="hero"`, icon on the left) then the quote button
|
|
13
|
+
* (`lead_cta_click` placement `hero`, id `quote-hero`): the same phone-first order as the header, the call bar and the
|
|
14
|
+
* CTA band (K6). Job mode only moves the primary styling (`data-primary`), never the order;
|
|
15
|
+
* - the actions are the page's call path (`data-bl-call-path`), so the phone call bar stays hidden while they are on
|
|
16
|
+
* screen and shows once they scroll out (F6);
|
|
17
|
+
* - with a form, the quote button links to it (`#quote-hero`, the form's own id) and the form is in the hero, so C1 sees
|
|
18
|
+
* the H1, a call and a quote action in the first phone screen and the form in the first desktop screen;
|
|
19
|
+
* - the photo is the page's largest image: preloaded, high priority, never lazy. It must be real work or licensed stock
|
|
20
|
+
* (an AI illustration is supporting only, F13), and a job caption needs a genuine photo;
|
|
21
|
+
* - no review quote, and no visible words the site doesn't pass.
|
|
22
|
+
*
|
|
23
|
+
* `family` is the page family (`data-family`): `home` takes no breadcrumbs and is `tall` by default; `service` and
|
|
24
|
+
* `location` take breadcrumbs. A skin styles families apart through `[data-family]`; the variant is the site's one
|
|
25
|
+
* hero structure.
|
|
26
|
+
*/
|
|
27
|
+
import type { ReactNode } from 'react';
|
|
28
|
+
import { type SiteJobMode } from '@brandlab-digital/site-core/strategy';
|
|
29
|
+
import { type SectionTone } from '../primitives/layout.js';
|
|
30
|
+
import { type TickItem } from '../primitives/lists.js';
|
|
31
|
+
import { type UiMedia } from '../primitives/media-frame.js';
|
|
32
|
+
import { type SkinEntry } from '../skin.js';
|
|
33
|
+
import type { UiPhone } from '../types.js';
|
|
34
|
+
import { type RatingChipData } from './rating-chip.js';
|
|
35
|
+
export declare const HERO_VARIANTS: readonly ["split", "full-bleed"];
|
|
36
|
+
export type HeroVariant = (typeof HERO_VARIANTS)[number];
|
|
37
|
+
/**
|
|
38
|
+
* The hero's family (`data-family`). Site-core's page families are finer (`service-in-suburb`, `region`, `suburb` all
|
|
39
|
+
* take the `location` hero), so this stays a hero-level grouping.
|
|
40
|
+
*/
|
|
41
|
+
export declare const HERO_FAMILIES: readonly ["home", "service", "location"];
|
|
42
|
+
export type HeroFamily = (typeof HERO_FAMILIES)[number];
|
|
43
|
+
/**
|
|
44
|
+
* A service's job mode (v2 §2.2), as the site's strategy decision card records it (`SiteJobMode` in
|
|
45
|
+
* `site-core/strategy`): which action is styled primary. Never the order.
|
|
46
|
+
*/
|
|
47
|
+
export declare const HERO_JOB_MODES: readonly ["urgent", "planned", "mixed"];
|
|
48
|
+
export type HeroJobMode = SiteJobMode;
|
|
49
|
+
export type HeroSlot = 'root' | 'inner' | 'content' | 'crumbs' | 'eyebrow' | 'h1' | 'summary' | 'actions' | 'phone' | 'quote' | 'checklist' | 'rating' | 'badges' | 'media' | 'caption' | 'scrim' | 'form';
|
|
50
|
+
export interface HeroLayout {
|
|
51
|
+
/** `split`: the photo's side on desktop (default `end`). */
|
|
52
|
+
readonly mediaSide?: 'start' | 'end';
|
|
53
|
+
/** Default `tall` on the home page, `standard` elsewhere. */
|
|
54
|
+
readonly height?: 'standard' | 'tall';
|
|
55
|
+
/** The trust checklist's columns (default 2). */
|
|
56
|
+
readonly checklistColumns?: 1 | 2;
|
|
57
|
+
}
|
|
58
|
+
export type HeroSkin = SkinEntry<HeroSlot, HeroVariant, HeroLayout>;
|
|
59
|
+
/** The form's id and lead diagnostics `form_id` in the hero; the quote button links here. */
|
|
60
|
+
export declare const HERO_FORM_ID = "quote-hero";
|
|
61
|
+
/** The most facts the trust checklist holds (v2 S1: three to six). */
|
|
62
|
+
export declare const HERO_CHECKLIST_MAX = 6;
|
|
63
|
+
/** The offer line's word limit (v2 §9.2). */
|
|
64
|
+
export declare const HERO_SUMMARY_MAX_WORDS = 25;
|
|
65
|
+
export interface HeroProps extends HeroSkin {
|
|
66
|
+
readonly family: HeroFamily;
|
|
67
|
+
/** The H1 text: the recorded H1 on live pages, the title builder's on new ones ("Tree Removal Castle Hill"). */
|
|
68
|
+
readonly title: string;
|
|
69
|
+
/** The one offer line under the H1, 25 words or fewer. */
|
|
70
|
+
readonly summary: string;
|
|
71
|
+
/** A short label above the H1. */
|
|
72
|
+
readonly eyebrow?: string;
|
|
73
|
+
/** The page's breadcrumbs (the shared `Breadcrumbs`, with its own `nav`). Service and location pages only. */
|
|
74
|
+
readonly breadcrumbs?: ReactNode;
|
|
75
|
+
/** The trust checklist: verified facts only (licence, insurance, rating source, years), at most six. */
|
|
76
|
+
readonly checklist: readonly TickItem[];
|
|
77
|
+
/**
|
|
78
|
+
* Google's average and total from the reviews feed (`prepareBusinessProfileReviews`), shown as the shared
|
|
79
|
+
* `RatingChip` (`data-bl="rating-chip"`, the `rating` class on its root, like `phone` and `quote`) after the
|
|
80
|
+
* checklist, linked to the Business Profile. Never typed by hand: without a feed average and a total of 1 or more,
|
|
81
|
+
* nothing renders.
|
|
82
|
+
*/
|
|
83
|
+
readonly rating?: RatingChipData | null;
|
|
84
|
+
/** Partner badges or logos held and verified (a `LogoStrip`). */
|
|
85
|
+
readonly badges?: ReactNode;
|
|
86
|
+
/** The best genuine photo of this page's service; on a location page the nearest job's. From `media.ts`. */
|
|
87
|
+
readonly media: UiMedia;
|
|
88
|
+
/** "{what}, {suburb}, {month year}" for a genuine job photo, for example on a location page. */
|
|
89
|
+
readonly caption?: string;
|
|
90
|
+
readonly phone: UiPhone;
|
|
91
|
+
/** Optional words before the number, for example "Call". */
|
|
92
|
+
readonly phoneLabel?: string;
|
|
93
|
+
/**
|
|
94
|
+
* The site's recorded quote label (C5). `href` defaults to the hero's own form (`#quote-hero`); without a form it
|
|
95
|
+
* is required (for example `/contact#quote`).
|
|
96
|
+
*/
|
|
97
|
+
readonly quote: {
|
|
98
|
+
readonly label: string;
|
|
99
|
+
readonly href?: string;
|
|
100
|
+
};
|
|
101
|
+
/** The site's bound quote form: `<QuoteForm formId="quote-hero" />`. Required on money pages (C1). */
|
|
102
|
+
readonly form?: ReactNode;
|
|
103
|
+
/** The page's service job mode (default `planned`): `urgent` styles the phone link primary. */
|
|
104
|
+
readonly jobMode?: HeroJobMode;
|
|
105
|
+
/** The H1's id (default `hero-title`). */
|
|
106
|
+
readonly headingId?: string;
|
|
107
|
+
/** Set by `SectionSequence`. */
|
|
108
|
+
readonly tone?: SectionTone;
|
|
109
|
+
/** Signature art, rendered `aria-hidden` (Superior's waterline, Absolute's grass edge). */
|
|
110
|
+
readonly decoration?: ReactNode;
|
|
111
|
+
}
|
|
112
|
+
export declare function Hero(props: HeroProps): import("react/jsx-runtime").JSX.Element;
|
|
113
|
+
//# sourceMappingURL=hero.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hero.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/hero.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SITE_JOB_MODES } from '@brandlab-digital/site-core/strategy';
|
|
3
|
+
import { Section } from '../primitives/layout.js';
|
|
4
|
+
import { PhoneLink } from '../primitives/links.js';
|
|
5
|
+
import { TickList } from '../primitives/lists.js';
|
|
6
|
+
import { MediaFrame } from '../primitives/media-frame.js';
|
|
7
|
+
import { QuoteButton } from '../primitives/quote-button.js';
|
|
8
|
+
import { slot } from '../skin.js';
|
|
9
|
+
import { RatingChip, hasGoogleRating } from './rating-chip.js';
|
|
10
|
+
export const HERO_VARIANTS = ['split', 'full-bleed'];
|
|
11
|
+
/**
|
|
12
|
+
* The hero's family (`data-family`). Site-core's page families are finer (`service-in-suburb`, `region`, `suburb` all
|
|
13
|
+
* take the `location` hero), so this stays a hero-level grouping.
|
|
14
|
+
*/
|
|
15
|
+
export const HERO_FAMILIES = ['home', 'service', 'location'];
|
|
16
|
+
/**
|
|
17
|
+
* A service's job mode (v2 §2.2), as the site's strategy decision card records it (`SiteJobMode` in
|
|
18
|
+
* `site-core/strategy`): which action is styled primary. Never the order.
|
|
19
|
+
*/
|
|
20
|
+
export const HERO_JOB_MODES = SITE_JOB_MODES;
|
|
21
|
+
/** The form's id and lead diagnostics `form_id` in the hero; the quote button links here. */
|
|
22
|
+
export const HERO_FORM_ID = 'quote-hero';
|
|
23
|
+
/** The most facts the trust checklist holds (v2 S1: three to six). */
|
|
24
|
+
export const HERO_CHECKLIST_MAX = 6;
|
|
25
|
+
/** The offer line's word limit (v2 §9.2). */
|
|
26
|
+
export const HERO_SUMMARY_MAX_WORDS = 25;
|
|
27
|
+
const words = (text) => text.trim().split(/\s+/).filter(Boolean).length;
|
|
28
|
+
function assertHero({ title, summary, checklist, media, caption, quote, form }) {
|
|
29
|
+
const problems = [];
|
|
30
|
+
if (title.trim() === '')
|
|
31
|
+
problems.push('the H1 is empty');
|
|
32
|
+
if (words(summary) > HERO_SUMMARY_MAX_WORDS)
|
|
33
|
+
problems.push(`the offer line has ${words(summary)} words (${HERO_SUMMARY_MAX_WORDS} at most)`);
|
|
34
|
+
if (checklist.length > HERO_CHECKLIST_MAX)
|
|
35
|
+
problems.push(`the checklist has ${checklist.length} facts (${HERO_CHECKLIST_MAX} at most)`);
|
|
36
|
+
if (media.provenance === 'illustrative-ai')
|
|
37
|
+
problems.push(`${media.src} is an AI illustration; the hero shows real work or licensed stock`);
|
|
38
|
+
if (caption && media.provenance !== 'genuine')
|
|
39
|
+
problems.push(`a job caption needs a genuine photo (${media.src} is ${media.provenance})`);
|
|
40
|
+
if (!form && !quote.href)
|
|
41
|
+
problems.push('without a form, quote.href must name the quote form, for example /contact#quote');
|
|
42
|
+
if (problems.length > 0)
|
|
43
|
+
throw new Error(`Hero "${title}": ${problems.join('; ')}.`);
|
|
44
|
+
}
|
|
45
|
+
export function Hero(props) {
|
|
46
|
+
assertHero(props);
|
|
47
|
+
const { family, title, summary, eyebrow, breadcrumbs, checklist, rating, badges, media, caption, phone, phoneLabel, quote, form, jobMode = 'planned', headingId = 'hero-title', tone, decoration, variant = 'split', layout, classes, } = props;
|
|
48
|
+
const primary = jobMode === 'urgent' ? 'phone' : 'quote';
|
|
49
|
+
const crumbs = family === 'home' ? null : breadcrumbs;
|
|
50
|
+
return (_jsxs(Section, { id: "hero", labelledBy: headingId, tone: tone, variant: variant, classes: classes, decoration: decoration, phonePlacement: "hero", hooks: {
|
|
51
|
+
'data-family': family,
|
|
52
|
+
'data-job-mode': jobMode,
|
|
53
|
+
'data-height': layout?.height ?? (family === 'home' ? 'tall' : 'standard'),
|
|
54
|
+
'data-media-side': variant === 'split' ? (layout?.mediaSide ?? 'end') : undefined,
|
|
55
|
+
'data-form': form ? '' : undefined,
|
|
56
|
+
}, children: [_jsxs("div", { ...slot(classes, 'content'), children: [crumbs ? _jsx("div", { ...slot(classes, 'crumbs'), children: crumbs }) : null, eyebrow ? _jsx("p", { ...slot(classes, 'eyebrow'), children: eyebrow }) : null, _jsx("h1", { id: headingId, ...slot(classes, 'h1'), children: title }), _jsx("p", { ...slot(classes, 'summary'), children: summary }), _jsxs("div", { ...slot(classes, 'actions'), "data-bl-call-path": "", "data-primary": primary, children: [_jsx(PhoneLink, { phone: phone, placement: "hero", label: phoneLabel, showIcon: true, variant: "button", classes: { root: classes?.phone } }), _jsx(QuoteButton, { label: quote.label, href: quote.href ?? `#${HERO_FORM_ID}`, placement: "hero", ctaId: "quote-hero", variant: primary === 'quote' ? 'primary' : 'secondary', classes: { root: classes?.quote } })] }), checklist.length > 0 ? (_jsx("div", { ...slot(classes, 'checklist'), children: _jsx(TickList, { items: checklist, layout: { columns: layout?.checklistColumns ?? 2 } }) })) : null, hasGoogleRating(rating) ? _jsx(RatingChip, { reviews: rating, classes: { root: classes?.rating } }) : null, badges ? _jsx("div", { ...slot(classes, 'badges'), children: badges }) : null] }), _jsxs("div", { ...slot(classes, 'media'), children: [_jsx(MediaFrame, { media: media, ratio: "4:3", sizes: variant === 'full-bleed' ? '100vw' : '(min-width: 1024px) 50vw, 100vw', caption: caption, priority: true, classes: { caption: classes?.caption } }), variant === 'full-bleed' ? _jsx("div", { ...slot(classes, 'scrim'), "aria-hidden": "true" }) : null] }), form ? _jsx("div", { ...slot(classes, 'form'), children: form }) : null] }));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=hero.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hero.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/hero.tsx"],"names":[],"mappings":";AA2BA,OAAO,EAAE,cAAc,EAAoB,MAAM,sCAAsC,CAAA;AACvF,OAAO,EAAE,OAAO,EAAoB,MAAM,yBAAyB,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAAE,QAAQ,EAAiB,MAAM,wBAAwB,CAAA;AAChE,OAAO,EAAE,UAAU,EAAgB,MAAM,8BAA8B,CAAA;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAC3D,OAAO,EAAE,IAAI,EAAkB,MAAM,YAAY,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAuB,MAAM,kBAAkB,CAAA;AAEnF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,YAAY,CAAU,CAAA;AAG7D;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAU,CAAA;AAGrE;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,cAAc,CAAA;AAiC5C,6FAA6F;AAC7F,MAAM,CAAC,MAAM,YAAY,GAAG,YAAY,CAAA;AACxC,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAA;AACnC,6CAA6C;AAC7C,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAA;AA+CxC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAA;AAE/E,SAAS,UAAU,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAa;IACvF,MAAM,QAAQ,GAAa,EAAE,CAAA;IAC7B,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;IACzD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,sBAAsB;QAAE,QAAQ,CAAC,IAAI,CAAC,sBAAsB,KAAK,CAAC,OAAO,CAAC,WAAW,sBAAsB,WAAW,CAAC,CAAA;IAC5I,IAAI,SAAS,CAAC,MAAM,GAAG,kBAAkB;QAAE,QAAQ,CAAC,IAAI,CAAC,qBAAqB,SAAS,CAAC,MAAM,WAAW,kBAAkB,WAAW,CAAC,CAAA;IACvI,IAAI,KAAK,CAAC,UAAU,KAAK,iBAAiB;QAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,oEAAoE,CAAC,CAAA;IAC3I,IAAI,OAAO,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;QAAE,QAAQ,CAAC,IAAI,CAAC,wCAAwC,KAAK,CAAC,GAAG,OAAO,KAAK,CAAC,UAAU,GAAG,CAAC,CAAA;IACzI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI;QAAE,QAAQ,CAAC,IAAI,CAAC,iFAAiF,CAAC,CAAA;IAC1H,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,SAAS,KAAK,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACtF,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,KAAgB;IACnC,UAAU,CAAC,KAAK,CAAC,CAAA;IACjB,MAAM,EACJ,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,WAAW,EACX,SAAS,EACT,MAAM,EACN,MAAM,EACN,KAAK,EACL,OAAO,EACP,KAAK,EACL,UAAU,EACV,KAAK,EACL,IAAI,EACJ,OAAO,GAAG,SAAS,EACnB,SAAS,GAAG,YAAY,EACxB,IAAI,EACJ,UAAU,EACV,OAAO,GAAG,OAAO,EACjB,MAAM,EACN,OAAO,GACR,GAAG,KAAK,CAAA;IACT,MAAM,OAAO,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;IACxD,MAAM,MAAM,GAAG,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAA;IACrD,OAAO,CACL,MAAC,OAAO,IACN,EAAE,EAAC,MAAM,EACT,UAAU,EAAE,SAAS,EACrB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU,EACtB,cAAc,EAAC,MAAM,EACrB,KAAK,EAAE;YACL,aAAa,EAAE,MAAM;YACrB,eAAe,EAAE,OAAO;YACxB,aAAa,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;YAC1E,iBAAiB,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;YACjF,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;SACnC,aAED,kBAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,aAC9B,MAAM,CAAC,CAAC,CAAC,iBAAS,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAG,MAAM,GAAO,CAAC,CAAC,CAAC,IAAI,EAChE,OAAO,CAAC,CAAC,CAAC,eAAO,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,YAAG,OAAO,GAAK,CAAC,CAAC,CAAC,IAAI,EAChE,aAAI,EAAE,EAAE,SAAS,KAAM,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,YACvC,KAAK,GACH,EACL,eAAO,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,YAAG,OAAO,GAAK,EAC9C,kBAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,uBAAoB,EAAE,kBAAe,OAAO,aAC3E,KAAC,SAAS,IAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAC,MAAM,EAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,QAAC,OAAO,EAAC,QAAQ,EAAC,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAI,EAC5H,KAAC,WAAW,IACV,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI,YAAY,EAAE,EACtC,SAAS,EAAC,MAAM,EAChB,KAAK,EAAC,YAAY,EAClB,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,EACtD,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GACjC,IACE,EACL,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACtB,iBAAS,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,YACjC,KAAC,QAAQ,IAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,IAAI,CAAC,EAAE,GAAI,GAC9E,CACP,CAAC,CAAC,CAAC,IAAI,EACP,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAC,UAAU,IAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,GAAI,CAAC,CAAC,CAAC,IAAI,EACpG,MAAM,CAAC,CAAC,CAAC,iBAAS,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAG,MAAM,GAAO,CAAC,CAAC,CAAC,IAAI,IAC7D,EACN,kBAAS,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,aAC7B,KAAC,UAAU,IACT,KAAK,EAAE,KAAK,EACZ,KAAK,EAAC,KAAK,EACX,KAAK,EAAE,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,iCAAiC,EAC7E,OAAO,EAAE,OAAO,EAChB,QAAQ,QACR,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,GACtC,EACD,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,iBAAS,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,iBAAc,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,IACrF,EACL,IAAI,CAAC,CAAC,CAAC,iBAAS,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,YAAG,IAAI,GAAO,CAAC,CAAC,CAAC,IAAI,IACnD,CACX,CAAA;AACH,CAAC","sourcesContent":["/**\n * `Hero` (S1, `hero`, 3.14.0): the first screen of the home, service and service-in-suburb pages. Server component;\n * it wins the click and starts the enquiry. Light by default: every colour comes from the site's tokens and skin.\n *\n * In source order, which is the phone order: breadcrumbs (service and location pages), an optional eyebrow, the H1\n * (once, as plain text; on a location page the built \"{Service} {Suburb}\" string), one offer line of 25 words or fewer,\n * the two actions, the trust checklist (up to six verified facts, two columns), Google's feed rating, partner badges,\n * the photo and then the site's quote form. On desktop the form sits beside the text (`split`, the photo on the other side) or over the photo\n * as a card (`full-bleed`, under a text-side scrim the skin colours).\n *\n * Locked:\n * - the actions are the phone link (`data-phone-location=\"hero\"`, icon on the left) then the quote button\n * (`lead_cta_click` placement `hero`, id `quote-hero`): the same phone-first order as the header, the call bar and the\n * CTA band (K6). Job mode only moves the primary styling (`data-primary`), never the order;\n * - the actions are the page's call path (`data-bl-call-path`), so the phone call bar stays hidden while they are on\n * screen and shows once they scroll out (F6);\n * - with a form, the quote button links to it (`#quote-hero`, the form's own id) and the form is in the hero, so C1 sees\n * the H1, a call and a quote action in the first phone screen and the form in the first desktop screen;\n * - the photo is the page's largest image: preloaded, high priority, never lazy. It must be real work or licensed stock\n * (an AI illustration is supporting only, F13), and a job caption needs a genuine photo;\n * - no review quote, and no visible words the site doesn't pass.\n *\n * `family` is the page family (`data-family`): `home` takes no breadcrumbs and is `tall` by default; `service` and\n * `location` take breadcrumbs. A skin styles families apart through `[data-family]`; the variant is the site's one\n * hero structure.\n */\nimport type { ReactNode } from 'react'\nimport { SITE_JOB_MODES, type SiteJobMode } from '@brandlab-digital/site-core/strategy'\nimport { Section, type SectionTone } from '../primitives/layout.js'\nimport { PhoneLink } from '../primitives/links.js'\nimport { TickList, type TickItem } from '../primitives/lists.js'\nimport { MediaFrame, type UiMedia } from '../primitives/media-frame.js'\nimport { QuoteButton } from '../primitives/quote-button.js'\nimport { slot, type SkinEntry } from '../skin.js'\nimport type { UiPhone } from '../types.js'\nimport { RatingChip, hasGoogleRating, type RatingChipData } from './rating-chip.js'\n\nexport const HERO_VARIANTS = ['split', 'full-bleed'] as const\nexport type HeroVariant = (typeof HERO_VARIANTS)[number]\n\n/**\n * The hero's family (`data-family`). Site-core's page families are finer (`service-in-suburb`, `region`, `suburb` all\n * take the `location` hero), so this stays a hero-level grouping.\n */\nexport const HERO_FAMILIES = ['home', 'service', 'location'] as const\nexport type HeroFamily = (typeof HERO_FAMILIES)[number]\n\n/**\n * A service's job mode (v2 §2.2), as the site's strategy decision card records it (`SiteJobMode` in\n * `site-core/strategy`): which action is styled primary. Never the order.\n */\nexport const HERO_JOB_MODES = SITE_JOB_MODES\nexport type HeroJobMode = SiteJobMode\n\nexport type HeroSlot =\n | 'root'\n | 'inner'\n | 'content'\n | 'crumbs'\n | 'eyebrow'\n | 'h1'\n | 'summary'\n | 'actions'\n | 'phone'\n | 'quote'\n | 'checklist'\n | 'rating'\n | 'badges'\n | 'media'\n | 'caption'\n | 'scrim'\n | 'form'\n\nexport interface HeroLayout {\n /** `split`: the photo's side on desktop (default `end`). */\n readonly mediaSide?: 'start' | 'end'\n /** Default `tall` on the home page, `standard` elsewhere. */\n readonly height?: 'standard' | 'tall'\n /** The trust checklist's columns (default 2). */\n readonly checklistColumns?: 1 | 2\n}\n\nexport type HeroSkin = SkinEntry<HeroSlot, HeroVariant, HeroLayout>\n\n/** The form's id and lead diagnostics `form_id` in the hero; the quote button links here. */\nexport const HERO_FORM_ID = 'quote-hero'\n/** The most facts the trust checklist holds (v2 S1: three to six). */\nexport const HERO_CHECKLIST_MAX = 6\n/** The offer line's word limit (v2 §9.2). */\nexport const HERO_SUMMARY_MAX_WORDS = 25\n\nexport interface HeroProps extends HeroSkin {\n readonly family: HeroFamily\n /** The H1 text: the recorded H1 on live pages, the title builder's on new ones (\"Tree Removal Castle Hill\"). */\n readonly title: string\n /** The one offer line under the H1, 25 words or fewer. */\n readonly summary: string\n /** A short label above the H1. */\n readonly eyebrow?: string\n /** The page's breadcrumbs (the shared `Breadcrumbs`, with its own `nav`). Service and location pages only. */\n readonly breadcrumbs?: ReactNode\n /** The trust checklist: verified facts only (licence, insurance, rating source, years), at most six. */\n readonly checklist: readonly TickItem[]\n /**\n * Google's average and total from the reviews feed (`prepareBusinessProfileReviews`), shown as the shared\n * `RatingChip` (`data-bl=\"rating-chip\"`, the `rating` class on its root, like `phone` and `quote`) after the\n * checklist, linked to the Business Profile. Never typed by hand: without a feed average and a total of 1 or more,\n * nothing renders.\n */\n readonly rating?: RatingChipData | null\n /** Partner badges or logos held and verified (a `LogoStrip`). */\n readonly badges?: ReactNode\n /** The best genuine photo of this page's service; on a location page the nearest job's. From `media.ts`. */\n readonly media: UiMedia\n /** \"{what}, {suburb}, {month year}\" for a genuine job photo, for example on a location page. */\n readonly caption?: string\n readonly phone: UiPhone\n /** Optional words before the number, for example \"Call\". */\n readonly phoneLabel?: string\n /**\n * The site's recorded quote label (C5). `href` defaults to the hero's own form (`#quote-hero`); without a form it\n * is required (for example `/contact#quote`).\n */\n readonly quote: { readonly label: string; readonly href?: string }\n /** The site's bound quote form: `<QuoteForm formId=\"quote-hero\" />`. Required on money pages (C1). */\n readonly form?: ReactNode\n /** The page's service job mode (default `planned`): `urgent` styles the phone link primary. */\n readonly jobMode?: HeroJobMode\n /** The H1's id (default `hero-title`). */\n readonly headingId?: string\n /** Set by `SectionSequence`. */\n readonly tone?: SectionTone\n /** Signature art, rendered `aria-hidden` (Superior's waterline, Absolute's grass edge). */\n readonly decoration?: ReactNode\n}\n\nconst words = (text: string) => text.trim().split(/\\s+/).filter(Boolean).length\n\nfunction assertHero({ title, summary, checklist, media, caption, quote, form }: HeroProps): void {\n const problems: string[] = []\n if (title.trim() === '') problems.push('the H1 is empty')\n if (words(summary) > HERO_SUMMARY_MAX_WORDS) problems.push(`the offer line has ${words(summary)} words (${HERO_SUMMARY_MAX_WORDS} at most)`)\n if (checklist.length > HERO_CHECKLIST_MAX) problems.push(`the checklist has ${checklist.length} facts (${HERO_CHECKLIST_MAX} at most)`)\n if (media.provenance === 'illustrative-ai') problems.push(`${media.src} is an AI illustration; the hero shows real work or licensed stock`)\n if (caption && media.provenance !== 'genuine') problems.push(`a job caption needs a genuine photo (${media.src} is ${media.provenance})`)\n if (!form && !quote.href) problems.push('without a form, quote.href must name the quote form, for example /contact#quote')\n if (problems.length > 0) throw new Error(`Hero \"${title}\": ${problems.join('; ')}.`)\n}\n\nexport function Hero(props: HeroProps) {\n assertHero(props)\n const {\n family,\n title,\n summary,\n eyebrow,\n breadcrumbs,\n checklist,\n rating,\n badges,\n media,\n caption,\n phone,\n phoneLabel,\n quote,\n form,\n jobMode = 'planned',\n headingId = 'hero-title',\n tone,\n decoration,\n variant = 'split',\n layout,\n classes,\n } = props\n const primary = jobMode === 'urgent' ? 'phone' : 'quote'\n const crumbs = family === 'home' ? null : breadcrumbs\n return (\n <Section\n id=\"hero\"\n labelledBy={headingId}\n tone={tone}\n variant={variant}\n classes={classes}\n decoration={decoration}\n phonePlacement=\"hero\"\n hooks={{\n 'data-family': family,\n 'data-job-mode': jobMode,\n 'data-height': layout?.height ?? (family === 'home' ? 'tall' : 'standard'),\n 'data-media-side': variant === 'split' ? (layout?.mediaSide ?? 'end') : undefined,\n 'data-form': form ? '' : undefined,\n }}\n >\n <div {...slot(classes, 'content')}>\n {crumbs ? <div {...slot(classes, 'crumbs')}>{crumbs}</div> : null}\n {eyebrow ? <p {...slot(classes, 'eyebrow')}>{eyebrow}</p> : null}\n <h1 id={headingId} {...slot(classes, 'h1')}>\n {title}\n </h1>\n <p {...slot(classes, 'summary')}>{summary}</p>\n <div {...slot(classes, 'actions')} data-bl-call-path=\"\" data-primary={primary}>\n <PhoneLink phone={phone} placement=\"hero\" label={phoneLabel} showIcon variant=\"button\" classes={{ root: classes?.phone }} />\n <QuoteButton\n label={quote.label}\n href={quote.href ?? `#${HERO_FORM_ID}`}\n placement=\"hero\"\n ctaId=\"quote-hero\"\n variant={primary === 'quote' ? 'primary' : 'secondary'}\n classes={{ root: classes?.quote }}\n />\n </div>\n {checklist.length > 0 ? (\n <div {...slot(classes, 'checklist')}>\n <TickList items={checklist} layout={{ columns: layout?.checklistColumns ?? 2 }} />\n </div>\n ) : null}\n {hasGoogleRating(rating) ? <RatingChip reviews={rating} classes={{ root: classes?.rating }} /> : null}\n {badges ? <div {...slot(classes, 'badges')}>{badges}</div> : null}\n </div>\n <div {...slot(classes, 'media')}>\n <MediaFrame\n media={media}\n ratio=\"4:3\"\n sizes={variant === 'full-bleed' ? '100vw' : '(min-width: 1024px) 50vw, 100vw'}\n caption={caption}\n priority\n classes={{ caption: classes?.caption }}\n />\n {variant === 'full-bleed' ? <div {...slot(classes, 'scrim')} aria-hidden=\"true\" /> : null}\n </div>\n {form ? <div {...slot(classes, 'form')}>{form}</div> : null}\n </Section>\n )\n}\n"]}
|
|
@@ -10,4 +10,6 @@ export { PROOF_STRIP_MINIMUM, PROOF_STRIP_VARIANTS, ProofStrip, proofStripReady,
|
|
|
10
10
|
export { PAGE_HEADER_VARIANTS, PageHeader, type PageHeaderProps, type PageHeaderSkin, type PageHeaderSlot, type PageHeaderVariant } from './page-header.js';
|
|
11
11
|
export { ContactBlock, type ContactBlockProps, type ContactBlockSkin, type ContactBlockSlot, type ContactDetailLines } from './contact-block.js';
|
|
12
12
|
export { SectionSequence, serialiseSectionManifest, type SectionSequenceProps, type SequenceSection } from './section-sequence.js';
|
|
13
|
+
export { HERO_CHECKLIST_MAX, HERO_FAMILIES, HERO_FORM_ID, HERO_JOB_MODES, HERO_SUMMARY_MAX_WORDS, HERO_VARIANTS, Hero, type HeroFamily, type HeroJobMode, type HeroLayout, type HeroProps, type HeroSkin, type HeroSlot, type HeroVariant, } from './hero.js';
|
|
14
|
+
export { SERVICES_GRID_CARD_LIMIT, SERVICES_GRID_VARIANTS, ServiceCard, ServicesGrid, defaultImageSizes, type ServiceCardData, type ServiceCardProps, type ServiceCardSlot, type ServicesGridLayout, type ServicesGridProps, type ServicesGridRepeatedSlot, type ServicesGridSkin, type ServicesGridSlot, type ServicesGridVariant, } from './services-grid.js';
|
|
13
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,4 +10,6 @@ export { PROOF_STRIP_MINIMUM, PROOF_STRIP_VARIANTS, ProofStrip, proofStripReady,
|
|
|
10
10
|
export { PAGE_HEADER_VARIANTS, PageHeader } from './page-header.js';
|
|
11
11
|
export { ContactBlock } from './contact-block.js';
|
|
12
12
|
export { SectionSequence, serialiseSectionManifest } from './section-sequence.js';
|
|
13
|
+
export { HERO_CHECKLIST_MAX, HERO_FAMILIES, HERO_FORM_ID, HERO_JOB_MODES, HERO_SUMMARY_MAX_WORDS, HERO_VARIANTS, Hero, } from './hero.js';
|
|
14
|
+
export { SERVICES_GRID_CARD_LIMIT, SERVICES_GRID_VARIANTS, ServiceCard, ServicesGrid, defaultImageSizes, } from './services-grid.js';
|
|
13
15
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +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"]}
|
|
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;AAClI,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,aAAa,EACb,IAAI,GAQL,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,WAAW,EACX,YAAY,EACZ,iBAAiB,GAUlB,MAAM,oBAAoB,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'\nexport {\n HERO_CHECKLIST_MAX,\n HERO_FAMILIES,\n HERO_FORM_ID,\n HERO_JOB_MODES,\n HERO_SUMMARY_MAX_WORDS,\n HERO_VARIANTS,\n Hero,\n type HeroFamily,\n type HeroJobMode,\n type HeroLayout,\n type HeroProps,\n type HeroSkin,\n type HeroSlot,\n type HeroVariant,\n} from './hero.js'\nexport {\n SERVICES_GRID_CARD_LIMIT,\n SERVICES_GRID_VARIANTS,\n ServiceCard,\n ServicesGrid,\n defaultImageSizes,\n type ServiceCardData,\n type ServiceCardProps,\n type ServiceCardSlot,\n type ServicesGridLayout,\n type ServicesGridProps,\n type ServicesGridRepeatedSlot,\n type ServicesGridSkin,\n type ServicesGridSlot,\n type ServicesGridVariant,\n} from './services-grid.js'\n"]}
|
|
@@ -11,13 +11,16 @@ import type { ReactNode } from 'react';
|
|
|
11
11
|
import { type BrandlabPrice } from '@brandlab-digital/site-core/prices';
|
|
12
12
|
import { type BrandlabSectionReadiness } from '@brandlab-digital/site-core/section-order';
|
|
13
13
|
import { type SectionHeadingSlot, type SectionSlot, type SectionTone } from '../primitives/layout.js';
|
|
14
|
+
import type { RepeatedSlotOf } from '../repeated-slots.js';
|
|
14
15
|
import { type SkinEntry } from '../skin.js';
|
|
15
16
|
import type { UiLink } from '../types.js';
|
|
16
17
|
import { type PriceLabels, type PriceTableSlot } from './price-table.js';
|
|
17
18
|
export declare const PRICE_GUIDE_VARIANTS: readonly ["table", "cards", "bands"];
|
|
18
19
|
export type PriceGuideVariant = (typeof PRICE_GUIDE_VARIANTS)[number];
|
|
19
20
|
export type PriceGuideSlot = SectionSlot | SectionHeadingSlot | Exclude<PriceTableSlot, 'root'> | 'cards' | 'card' | 'title' | 'figures' | 'bands' | 'band' | 'factors' | 'subheading' | 'factor' | 'name' | 'effect' | 'detail' | 'more' | 'link';
|
|
20
|
-
|
|
21
|
+
/** Rendered once per price row, card, band or factor: styled from the skin stylesheet, never by class (3.14.0). */
|
|
22
|
+
export type PriceGuideRepeatedSlot = RepeatedSlotOf<'priceGuide'>;
|
|
23
|
+
export type PriceGuideSkin = SkinEntry<PriceGuideSlot, PriceGuideVariant, Record<never, never>, PriceGuideRepeatedSlot>;
|
|
21
24
|
/** Something that moves the price. `effect` only when the owner supplied the dollar figure. */
|
|
22
25
|
export interface PriceFactor {
|
|
23
26
|
readonly label: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"price-guide.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/price-guide.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,EAAoD,KAAK,aAAa,EAAE,MAAM,oCAAoC,CAAA;AACzH,OAAO,EAAiB,KAAK,wBAAwB,EAAE,MAAM,2CAA2C,CAAA;AAExG,OAAO,EAA2B,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC9H,OAAO,
|
|
1
|
+
{"version":3,"file":"price-guide.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/price-guide.tsx"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { PRICE_CAPTION, formatPriceBand, renderablePrices } from '@brandlab-digi
|
|
|
3
3
|
import { SECTION_READY } from '@brandlab-digital/site-core/section-order';
|
|
4
4
|
import { TextLink } from '../primitives/links.js';
|
|
5
5
|
import { Section, SectionHeading } from '../primitives/layout.js';
|
|
6
|
-
import { slot } from '../skin.js';
|
|
6
|
+
import { repeatedSlot, slot } from '../skin.js';
|
|
7
7
|
import { PriceFigures, PriceScope, PriceTable, assertPriceWording } from './price-table.js';
|
|
8
8
|
export const PRICE_GUIDE_VARIANTS = ['table', 'cards', 'bands'];
|
|
9
9
|
/** Whether the section renders: nothing without a renderable band (`no-approved-prices`). */
|
|
@@ -20,9 +20,9 @@ export function PriceGuide({ title, eyebrow, lead, prices, service, labels, capt
|
|
|
20
20
|
body = _jsx(PriceTable, { prices: rows, labels: labels, caption: caption, classes: { ...classes, root: undefined } });
|
|
21
21
|
}
|
|
22
22
|
else {
|
|
23
|
-
const list = (row) => variant === 'cards' ? (_jsxs("li", { ...
|
|
23
|
+
const list = (row) => variant === 'cards' ? (_jsxs("li", { ...repeatedSlot('card'), children: [_jsx("h3", { ...repeatedSlot('title'), children: row.job }), _jsx("div", { ...repeatedSlot('figures'), children: _jsx(PriceFigures, { price: row, labels: labels }) }), _jsx(PriceScope, { price: row, labels: labels })] }, row.job)) : (_jsxs("li", { ...repeatedSlot('band'), children: [_jsx("span", { ...repeatedSlot('job'), children: row.job }), ' ', _jsx("div", { ...repeatedSlot('figures'), children: _jsx(PriceFigures, { price: row, labels: labels }) }), _jsx(PriceScope, { price: row, labels: labels })] }, row.job));
|
|
24
24
|
body = (_jsxs(_Fragment, { children: [_jsx("p", { ...slot(classes, 'caption'), children: caption }), _jsx("ul", { role: "list", ...slot(classes, variant), children: rows.map(list) })] }));
|
|
25
25
|
}
|
|
26
|
-
return (_jsxs(Section, { id: "price-guide", labelledBy: headingId, tone: tone, variant: variant, classes: classes, children: [_jsx(SectionHeading, { id: headingId, title: title, eyebrow: eyebrow, lead: lead, classes: classes }), body, factors && factors.items.length > 0 ? (_jsxs("div", { ...slot(classes, 'factors'), children: [_jsx("h3", { ...slot(classes, 'subheading'), children: factors.title }), _jsx("ul", { children: factors.items.map((factor) => (_jsxs("li", { ...
|
|
26
|
+
return (_jsxs(Section, { id: "price-guide", labelledBy: headingId, tone: tone, variant: variant, classes: classes, children: [_jsx(SectionHeading, { id: headingId, title: title, eyebrow: eyebrow, lead: lead, classes: classes }), body, factors && factors.items.length > 0 ? (_jsxs("div", { ...slot(classes, 'factors'), children: [_jsx("h3", { ...slot(classes, 'subheading'), children: factors.title }), _jsx("ul", { children: factors.items.map((factor) => (_jsxs("li", { ...repeatedSlot('factor'), children: [_jsx("span", { ...repeatedSlot('name'), children: factor.label }), factor.effect ? (_jsxs(_Fragment, { children: [' ', _jsx("span", { ...repeatedSlot('effect'), children: formatPriceBand(factor.effect) })] })) : null, factor.detail ? _jsx("p", { ...repeatedSlot('detail'), children: factor.detail }) : null] }, factor.label))) })] })) : null, costGuide ? (_jsx("p", { ...slot(classes, 'more'), children: _jsx(TextLink, { href: costGuide.href, variant: "standalone", classes: { root: classes?.link }, children: costGuide.label }) })) : null] }));
|
|
27
27
|
}
|
|
28
28
|
//# sourceMappingURL=price-guide.js.map
|