@brandlab-digital/site-next 3.15.1 → 3.16.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/navigation/use-sticky-call-bar.d.ts +6 -1
- package/dist/navigation/use-sticky-call-bar.d.ts.map +1 -1
- package/dist/navigation/use-sticky-call-bar.js +9 -4
- package/dist/navigation/use-sticky-call-bar.js.map +1 -1
- package/dist/ui-kit/brandlab-skin.d.ts +2 -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/repeated-slots.d.ts +1 -1
- package/dist/ui-kit/repeated-slots.js +1 -1
- package/dist/ui-kit/repeated-slots.js.map +1 -1
- package/dist/ui-kit/sections/cta-band.d.ts +10 -2
- package/dist/ui-kit/sections/cta-band.d.ts.map +1 -1
- package/dist/ui-kit/sections/cta-band.js +2 -2
- package/dist/ui-kit/sections/cta-band.js.map +1 -1
- package/dist/ui-kit/sections/hero.d.ts +43 -2
- package/dist/ui-kit/sections/hero.d.ts.map +1 -1
- package/dist/ui-kit/sections/hero.js +86 -3
- package/dist/ui-kit/sections/hero.js.map +1 -1
- package/dist/ui-kit/sections/index.d.ts +3 -2
- package/dist/ui-kit/sections/index.d.ts.map +1 -1
- package/dist/ui-kit/sections/index.js +3 -2
- package/dist/ui-kit/sections/index.js.map +1 -1
- package/dist/ui-kit/sections/proof-strip.d.ts +7 -1
- package/dist/ui-kit/sections/proof-strip.d.ts.map +1 -1
- package/dist/ui-kit/sections/proof-strip.js +6 -2
- package/dist/ui-kit/sections/proof-strip.js.map +1 -1
- package/dist/ui-kit/sections/rating-chip.d.ts +15 -5
- package/dist/ui-kit/sections/rating-chip.d.ts.map +1 -1
- package/dist/ui-kit/sections/rating-chip.js +10 -4
- package/dist/ui-kit/sections/rating-chip.js.map +1 -1
- package/dist/ui-kit/sections/section-sequence.d.ts +3 -1
- package/dist/ui-kit/sections/section-sequence.d.ts.map +1 -1
- package/dist/ui-kit/sections/section-sequence.js +4 -2
- package/dist/ui-kit/sections/section-sequence.js.map +1 -1
- package/dist/ui-kit/sections/signature-section.d.ts +42 -0
- package/dist/ui-kit/sections/signature-section.d.ts.map +1 -0
- package/dist/ui-kit/sections/signature-section.js +14 -0
- package/dist/ui-kit/sections/signature-section.js.map +1 -0
- package/dist/ui-kit/shell/call-bar-client.d.ts +2 -1
- package/dist/ui-kit/shell/call-bar-client.d.ts.map +1 -1
- package/dist/ui-kit/shell/call-bar-client.js +3 -3
- package/dist/ui-kit/shell/call-bar-client.js.map +1 -1
- package/dist/ui-kit/shell/mobile-call-bar.d.ts +6 -2
- package/dist/ui-kit/shell/mobile-call-bar.d.ts.map +1 -1
- package/dist/ui-kit/shell/mobile-call-bar.js +5 -2
- package/dist/ui-kit/shell/mobile-call-bar.js.map +1 -1
- package/dist/ui-kit/structure.css +137 -2
- package/package.json +2 -2
|
@@ -1,4 +1,44 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* `Hero` (S1, `hero`, 3.14.0): the first screen of the home, service and service-in-suburb pages. Server component;
|
|
4
|
+
* it wins the click and starts the enquiry. Light by default: every colour comes from the site's tokens and skin.
|
|
5
|
+
*
|
|
6
|
+
* In source order, which is the phone order: breadcrumbs (service and location pages), an optional eyebrow, the H1
|
|
7
|
+
* (once, as plain text; on a location page the built "{Service} {Suburb}" string), one offer line of 25 words or fewer,
|
|
8
|
+
* the two actions, the trust checklist (up to six verified facts, two columns), Google's feed rating, partner badges,
|
|
9
|
+
* 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
|
|
10
|
+
* as a card (`full-bleed`, under a text-side scrim the skin colours).
|
|
11
|
+
*
|
|
12
|
+
* Locked:
|
|
13
|
+
* - the actions are the phone link (`data-phone-location="hero"`, icon on the left) then the quote button
|
|
14
|
+
* (`lead_cta_click` placement `hero`, id `quote-hero`): the same phone-first order as the header, the call bar and the
|
|
15
|
+
* CTA band (K6). Job mode only moves the primary styling (`data-primary`), never the order;
|
|
16
|
+
* - the actions are the page's call path (`data-bl-call-path`), so the phone call bar stays hidden while they are on
|
|
17
|
+
* screen and shows once they scroll out (F6);
|
|
18
|
+
* - 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
|
|
19
|
+
* the H1, a call and a quote action in the first phone screen and the form in the first desktop screen;
|
|
20
|
+
* - the photo is the page's largest image: preloaded, high priority, never lazy. It must be real work or licensed stock
|
|
21
|
+
* (an AI illustration is supporting only, F13), and a job caption needs a genuine photo;
|
|
22
|
+
* - no review quote, and no visible words the site doesn't pass.
|
|
23
|
+
*
|
|
24
|
+
* `family` is the page family (`data-family`): `home` takes no breadcrumbs and is `tall` by default; `service` and
|
|
25
|
+
* `location` take breadcrumbs. A skin styles families apart through `[data-family]`; the variant is the site's one
|
|
26
|
+
* hero structure.
|
|
27
|
+
*
|
|
28
|
+
* The site's own hero (3.16.0), inside the same skeleton:
|
|
29
|
+
* - `layout.contentOrder` arranges the text column's parts (AA: H1, rating, summary, checklist, actions). The
|
|
30
|
+
* breadcrumbs stay first, an eyebrow sits straight before the H1, and the H1 comes before the summary and actions;
|
|
31
|
+
* parts left out follow in the default order;
|
|
32
|
+
* - `layout.rating: 'figure'` shows Google's rating as a large figure with the stars and count, and `ratingMoreLabel`
|
|
33
|
+
* adds the site's link to all the reviews;
|
|
34
|
+
* - `formHeading` puts an eyebrow, an H2 and a line above the quote form (the hero's one H2);
|
|
35
|
+
* - `seal` stamps one verified fact on the photo (`{ value: '15+', label: 'years of tree work' }`);
|
|
36
|
+
* - `layout.scrim` chooses the overlay: `full` over the whole photo, `column` behind the text side only, or `none`
|
|
37
|
+
* (the default on `split`; `full-bleed` defaults to `full`), coloured by the skin's `--bl-hero-scrim`;
|
|
38
|
+
* - the photo's corners, shadow and filter come from `--bl-hero-photo-radius`, `--bl-hero-photo-shadow` and
|
|
39
|
+
* `--bl-hero-photo-filter`, and the `decoration` slot holds the site's motif.
|
|
40
|
+
*/
|
|
41
|
+
import { Fragment } from 'react';
|
|
2
42
|
import { SITE_JOB_MODES } from '@brandlab-digital/site-core/strategy';
|
|
3
43
|
import { Section } from '../primitives/layout.js';
|
|
4
44
|
import { PhoneLink } from '../primitives/links.js';
|
|
@@ -7,6 +47,9 @@ import { MediaFrame } from '../primitives/media-frame.js';
|
|
|
7
47
|
import { QuoteButton } from '../primitives/quote-button.js';
|
|
8
48
|
import { slot } from '../skin.js';
|
|
9
49
|
import { RatingChip, hasGoogleRating } from './rating-chip.js';
|
|
50
|
+
/** The text column's parts, in their default order (3.16.0). The breadcrumbs always come first. */
|
|
51
|
+
export const HERO_CONTENT_PARTS = ['eyebrow', 'h1', 'summary', 'actions', 'checklist', 'rating', 'badges'];
|
|
52
|
+
export const HERO_SCRIMS = ['none', 'full', 'column'];
|
|
10
53
|
export const HERO_VARIANTS = ['split', 'full-bleed'];
|
|
11
54
|
/**
|
|
12
55
|
* The hero's family (`data-family`). Site-core's page families are finer (`service-in-suburb`, `region`, `suburb` all
|
|
@@ -24,8 +67,31 @@ export const HERO_FORM_ID = 'quote-hero';
|
|
|
24
67
|
export const HERO_CHECKLIST_MAX = 6;
|
|
25
68
|
/** The offer line's word limit (v2 §9.2). */
|
|
26
69
|
export const HERO_SUMMARY_MAX_WORDS = 25;
|
|
70
|
+
/** The form heading's id (3.16.0). */
|
|
71
|
+
export const HERO_FORM_HEADING_ID = `${HERO_FORM_ID}-title`;
|
|
72
|
+
/** The text column's order: the site's order, then the parts it left out in the default order. */
|
|
73
|
+
export function heroContentOrder(order = []) {
|
|
74
|
+
const problems = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
for (const part of order) {
|
|
77
|
+
if (!HERO_CONTENT_PARTS.includes(part))
|
|
78
|
+
problems.push(`"${part}" is not a hero part (${HERO_CONTENT_PARTS.join(', ')})`);
|
|
79
|
+
else if (seen.has(part))
|
|
80
|
+
problems.push(`"${part}" appears twice`);
|
|
81
|
+
seen.add(part);
|
|
82
|
+
}
|
|
83
|
+
const full = [...order, ...HERO_CONTENT_PARTS.filter((part) => !seen.has(part))];
|
|
84
|
+
const at = (part) => full.indexOf(part);
|
|
85
|
+
if (at('eyebrow') !== at('h1') - 1)
|
|
86
|
+
problems.push('the eyebrow sits straight before the H1');
|
|
87
|
+
if (at('summary') < at('h1') || at('actions') < at('h1'))
|
|
88
|
+
problems.push('the H1 comes before the summary and the actions');
|
|
89
|
+
if (problems.length > 0)
|
|
90
|
+
throw new Error(`Hero content order: ${problems.join('; ')}.`);
|
|
91
|
+
return full;
|
|
92
|
+
}
|
|
27
93
|
const words = (text) => text.trim().split(/\s+/).filter(Boolean).length;
|
|
28
|
-
function assertHero({ title, summary, checklist, media, caption, quote, form }) {
|
|
94
|
+
function assertHero({ title, summary, checklist, media, caption, quote, form, formHeading, seal }) {
|
|
29
95
|
const problems = [];
|
|
30
96
|
if (title.trim() === '')
|
|
31
97
|
problems.push('the H1 is empty');
|
|
@@ -39,20 +105,37 @@ function assertHero({ title, summary, checklist, media, caption, quote, form })
|
|
|
39
105
|
problems.push(`a job caption needs a genuine photo (${media.src} is ${media.provenance})`);
|
|
40
106
|
if (!form && !quote.href)
|
|
41
107
|
problems.push('without a form, quote.href must name the quote form, for example /contact#quote');
|
|
108
|
+
if (formHeading && !form)
|
|
109
|
+
problems.push('a form heading needs the form');
|
|
110
|
+
if (formHeading && formHeading.title.trim() === '')
|
|
111
|
+
problems.push('the form heading is empty');
|
|
112
|
+
if (seal && (seal.value.trim() === '' || seal.label.trim() === ''))
|
|
113
|
+
problems.push('the seal needs a value and a label');
|
|
42
114
|
if (problems.length > 0)
|
|
43
115
|
throw new Error(`Hero "${title}": ${problems.join('; ')}.`);
|
|
44
116
|
}
|
|
45
117
|
export function Hero(props) {
|
|
46
118
|
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;
|
|
119
|
+
const { family, title, summary, eyebrow, breadcrumbs, checklist, rating, badges, media, caption, phone, phoneLabel, quote, form, jobMode = 'planned', headingId = 'hero-title', tone, decoration, ratingMoreLabel, formHeading, seal, variant = 'split', layout, classes, } = props;
|
|
48
120
|
const primary = jobMode === 'urgent' ? 'phone' : 'quote';
|
|
49
121
|
const crumbs = family === 'home' ? null : breadcrumbs;
|
|
122
|
+
const scrim = layout?.scrim ?? (variant === 'full-bleed' ? 'full' : 'none');
|
|
123
|
+
const parts = {
|
|
124
|
+
eyebrow: eyebrow ? _jsx("p", { ...slot(classes, 'eyebrow'), children: eyebrow }) : null,
|
|
125
|
+
h1: (_jsx("h1", { id: headingId, ...slot(classes, 'h1'), children: title })),
|
|
126
|
+
summary: _jsx("p", { ...slot(classes, 'summary'), children: summary }),
|
|
127
|
+
actions: (_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 } })] })),
|
|
128
|
+
checklist: checklist.length > 0 ? (_jsx("div", { ...slot(classes, 'checklist'), children: _jsx(TickList, { items: checklist, layout: { columns: layout?.checklistColumns ?? 2, ...(layout?.checklistTick ? { tick: layout.checklistTick } : {}) } }) })) : null,
|
|
129
|
+
rating: hasGoogleRating(rating) ? (_jsx(RatingChip, { reviews: rating, moreLabel: ratingMoreLabel, layout: layout?.rating === 'figure' ? { display: 'figure' } : undefined, classes: { root: classes?.rating } })) : null,
|
|
130
|
+
badges: badges ? _jsx("div", { ...slot(classes, 'badges'), children: badges }) : null,
|
|
131
|
+
};
|
|
50
132
|
return (_jsxs(Section, { id: "hero", labelledBy: headingId, tone: tone, variant: variant, classes: classes, decoration: decoration, phonePlacement: "hero", hooks: {
|
|
51
133
|
'data-family': family,
|
|
52
134
|
'data-job-mode': jobMode,
|
|
53
135
|
'data-height': layout?.height ?? (family === 'home' ? 'tall' : 'standard'),
|
|
54
136
|
'data-media-side': variant === 'split' ? (layout?.mediaSide ?? 'end') : undefined,
|
|
55
137
|
'data-form': form ? '' : undefined,
|
|
56
|
-
|
|
138
|
+
'data-scrim': scrim === 'none' ? undefined : scrim,
|
|
139
|
+
}, children: [_jsxs("div", { ...slot(classes, 'content'), children: [crumbs ? _jsx("div", { ...slot(classes, 'crumbs'), children: crumbs }) : null, heroContentOrder(layout?.contentOrder).map((part) => (_jsx(Fragment, { children: parts[part] }, part)))] }), _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 } }), scrim === 'none' ? null : _jsx("div", { ...slot(classes, 'scrim'), "aria-hidden": "true" }), seal ? (_jsxs("p", { ...slot(classes, 'seal'), children: [_jsx("span", { ...slot(classes, 'sealValue'), children: seal.value }), " ", _jsx("span", { ...slot(classes, 'sealLabel'), children: seal.label })] })) : null] }), form ? (_jsxs("div", { ...slot(classes, 'form'), children: [formHeading ? (_jsxs("div", { ...slot(classes, 'formHead'), children: [formHeading.eyebrow ? _jsx("p", { ...slot(classes, 'formEyebrow'), children: formHeading.eyebrow }) : null, _jsx("h2", { id: HERO_FORM_HEADING_ID, ...slot(classes, 'formTitle'), children: formHeading.title }), formHeading.text ? _jsx("p", { ...slot(classes, 'formText'), children: formHeading.text }) : null] })) : null, form] })) : null] }));
|
|
57
140
|
}
|
|
58
141
|
//# sourceMappingURL=hero.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hero.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/hero.tsx"],"names":[],"mappings":"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","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 * The checklist's ticks (3.15.0): `icon` (the default) is the sprite's check; `rule` ships no svg, only a mark the\n * stylesheet draws as a short rule (`TickList`'s `layout.tick`), about 100 bytes less a fact.\n */\n readonly checklistTick?: 'icon' | 'rule'\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, ...(layout?.checklistTick ? { tick: layout.checklistTick } : {}) }} />\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"]}
|
|
1
|
+
{"version":3,"file":"hero.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/hero.tsx"],"names":[],"mappings":"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","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 *\n * The site's own hero (3.16.0), inside the same skeleton:\n * - `layout.contentOrder` arranges the text column's parts (AA: H1, rating, summary, checklist, actions). The\n * breadcrumbs stay first, an eyebrow sits straight before the H1, and the H1 comes before the summary and actions;\n * parts left out follow in the default order;\n * - `layout.rating: 'figure'` shows Google's rating as a large figure with the stars and count, and `ratingMoreLabel`\n * adds the site's link to all the reviews;\n * - `formHeading` puts an eyebrow, an H2 and a line above the quote form (the hero's one H2);\n * - `seal` stamps one verified fact on the photo (`{ value: '15+', label: 'years of tree work' }`);\n * - `layout.scrim` chooses the overlay: `full` over the whole photo, `column` behind the text side only, or `none`\n * (the default on `split`; `full-bleed` defaults to `full`), coloured by the skin's `--bl-hero-scrim`;\n * - the photo's corners, shadow and filter come from `--bl-hero-photo-radius`, `--bl-hero-photo-shadow` and\n * `--bl-hero-photo-filter`, and the `decoration` slot holds the site's motif.\n */\nimport { Fragment, 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\n/** The text column's parts, in their default order (3.16.0). The breadcrumbs always come first. */\nexport const HERO_CONTENT_PARTS = ['eyebrow', 'h1', 'summary', 'actions', 'checklist', 'rating', 'badges'] as const\nexport type HeroContentPart = (typeof HERO_CONTENT_PARTS)[number]\n\nexport const HERO_SCRIMS = ['none', 'full', 'column'] as const\nexport type HeroScrim = (typeof HERO_SCRIMS)[number]\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 | 'formHead'\n | 'formEyebrow'\n | 'formTitle'\n | 'formText'\n | 'seal'\n | 'sealValue'\n | 'sealLabel'\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 * The checklist's ticks (3.15.0): `icon` (the default) is the sprite's check; `rule` ships no svg, only a mark the\n * stylesheet draws as a short rule (`TickList`'s `layout.tick`), about 100 bytes less a fact.\n */\n readonly checklistTick?: 'icon' | 'rule'\n /** The text column's order (3.16.0); parts left out follow in the default order. */\n readonly contentOrder?: readonly HeroContentPart[]\n /** Google's rating as one line (`chip`, the default) or a large figure with the stars and count (3.16.0). */\n readonly rating?: 'chip' | 'figure'\n /** The photo's overlay (3.16.0): `full`, `column` (behind the text side only) or `none`. */\n readonly scrim?: HeroScrim\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 /** The site's words for a link to all the reviews on Google, after the rating (3.16.0). */\n readonly ratingMoreLabel?: string\n /** A heading block above the quote form (3.16.0): an optional eyebrow, the H2 and an optional line. */\n readonly formHeading?: { readonly eyebrow?: string; readonly title: string; readonly text?: string }\n /** One verified fact stamped on the photo (3.16.0), for example `{ value: '15+', label: 'years of tree work' }`. */\n readonly seal?: { readonly value: string; readonly label: string }\n}\n\n/** The form heading's id (3.16.0). */\nexport const HERO_FORM_HEADING_ID = `${HERO_FORM_ID}-title`\n\n/** The text column's order: the site's order, then the parts it left out in the default order. */\nexport function heroContentOrder(order: readonly HeroContentPart[] = []): HeroContentPart[] {\n const problems: string[] = []\n const seen = new Set<string>()\n for (const part of order) {\n if (!(HERO_CONTENT_PARTS as readonly string[]).includes(part)) problems.push(`\"${part}\" is not a hero part (${HERO_CONTENT_PARTS.join(', ')})`)\n else if (seen.has(part)) problems.push(`\"${part}\" appears twice`)\n seen.add(part)\n }\n const full = [...order, ...HERO_CONTENT_PARTS.filter((part) => !seen.has(part))]\n const at = (part: HeroContentPart) => full.indexOf(part)\n if (at('eyebrow') !== at('h1') - 1) problems.push('the eyebrow sits straight before the H1')\n if (at('summary') < at('h1') || at('actions') < at('h1')) problems.push('the H1 comes before the summary and the actions')\n if (problems.length > 0) throw new Error(`Hero content order: ${problems.join('; ')}.`)\n return full\n}\n\nconst words = (text: string) => text.trim().split(/\\s+/).filter(Boolean).length\n\nfunction assertHero({ title, summary, checklist, media, caption, quote, form, formHeading, seal }: 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 (formHeading && !form) problems.push('a form heading needs the form')\n if (formHeading && formHeading.title.trim() === '') problems.push('the form heading is empty')\n if (seal && (seal.value.trim() === '' || seal.label.trim() === '')) problems.push('the seal needs a value and a label')\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 ratingMoreLabel,\n formHeading,\n seal,\n variant = 'split',\n layout,\n classes,\n } = props\n const primary = jobMode === 'urgent' ? 'phone' : 'quote'\n const crumbs = family === 'home' ? null : breadcrumbs\n const scrim = layout?.scrim ?? (variant === 'full-bleed' ? 'full' : 'none')\n const parts: Record<HeroContentPart, ReactNode> = {\n eyebrow: eyebrow ? <p {...slot(classes, 'eyebrow')}>{eyebrow}</p> : null,\n h1: (\n <h1 id={headingId} {...slot(classes, 'h1')}>\n {title}\n </h1>\n ),\n summary: <p {...slot(classes, 'summary')}>{summary}</p>,\n actions: (\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 ),\n checklist:\n checklist.length > 0 ? (\n <div {...slot(classes, 'checklist')}>\n <TickList items={checklist} layout={{ columns: layout?.checklistColumns ?? 2, ...(layout?.checklistTick ? { tick: layout.checklistTick } : {}) }} />\n </div>\n ) : null,\n rating: hasGoogleRating(rating) ? (\n <RatingChip\n reviews={rating}\n moreLabel={ratingMoreLabel}\n layout={layout?.rating === 'figure' ? { display: 'figure' } : undefined}\n classes={{ root: classes?.rating }}\n />\n ) : null,\n badges: badges ? <div {...slot(classes, 'badges')}>{badges}</div> : null,\n }\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 'data-scrim': scrim === 'none' ? undefined : scrim,\n }}\n >\n <div {...slot(classes, 'content')}>\n {crumbs ? <div {...slot(classes, 'crumbs')}>{crumbs}</div> : null}\n {heroContentOrder(layout?.contentOrder).map((part) => (\n <Fragment key={part}>{parts[part]}</Fragment>\n ))}\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 {scrim === 'none' ? null : <div {...slot(classes, 'scrim')} aria-hidden=\"true\" />}\n {seal ? (\n <p {...slot(classes, 'seal')}>\n <span {...slot(classes, 'sealValue')}>{seal.value}</span> <span {...slot(classes, 'sealLabel')}>{seal.label}</span>\n </p>\n ) : null}\n </div>\n {form ? (\n <div {...slot(classes, 'form')}>\n {formHeading ? (\n <div {...slot(classes, 'formHead')}>\n {formHeading.eyebrow ? <p {...slot(classes, 'formEyebrow')}>{formHeading.eyebrow}</p> : null}\n <h2 id={HERO_FORM_HEADING_ID} {...slot(classes, 'formTitle')}>\n {formHeading.title}\n </h2>\n {formHeading.text ? <p {...slot(classes, 'formText')}>{formHeading.text}</p> : null}\n </div>\n ) : null}\n {form}\n </div>\n ) : null}\n </Section>\n )\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { hasGoogleRating, RatingChip, type RatingChipData, type RatingChipProps, type RatingChipSkin, type RatingChipSlot } from './rating-chip.js';
|
|
1
|
+
export { hasGoogleRating, RatingChip, type RatingChipData, type RatingChipLayout, type RatingChipProps, type RatingChipSkin, type RatingChipSlot, } from './rating-chip.js';
|
|
2
2
|
export { REVIEWS_BAND_HEADING_ID, REVIEWS_BAND_SKIP_REASON, REVIEWS_BAND_VARIANTS, ReviewsBand, ReviewsBandHeading, ReviewsBandItem, ReviewsBandList, ReviewsBandRoot, ReviewsBandSummary, reviewsBandReady, splitReviewText, type ReviewsBandHeadingProps, type ReviewsBandItemProps, type ReviewsBandLayout, type ReviewsBandProps, type ReviewsBandRootProps, type ReviewsBandSkin, type ReviewsBandSlot, type ReviewsBandSummaryProps, type ReviewsBandVariant, } from './reviews-band.js';
|
|
3
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
4
|
export { SITE_FOOTER_VARIANTS, SiteFooter, type SiteFooterColumn, type SiteFooterLicence, type SiteFooterProps, type SiteFooterSkin, type SiteFooterSlot, type SiteFooterVariant, } from './site-footer.js';
|
|
@@ -10,7 +10,7 @@ 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';
|
|
13
|
+
export { HERO_CHECKLIST_MAX, HERO_CONTENT_PARTS, HERO_FAMILIES, HERO_FORM_HEADING_ID, HERO_FORM_ID, HERO_JOB_MODES, HERO_SUMMARY_MAX_WORDS, HERO_SCRIMS, HERO_VARIANTS, Hero, heroContentOrder, type HeroContentPart, type HeroFamily, type HeroJobMode, type HeroLayout, type HeroProps, type HeroScrim, type HeroSkin, type HeroSlot, type HeroVariant, } from './hero.js';
|
|
14
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';
|
|
15
15
|
export { CouncilRecordLines, CouncilRules, councilFeeLine, councilRecordReady, councilRulesReady, formatCouncilFeeAmount, type CouncilRecordSlot, type CouncilRuleWording, type CouncilRulesProps, type CouncilRulesRepeatedSlot, type CouncilRulesSkin, type CouncilRulesSlot, } from './council-rules.js';
|
|
16
16
|
export { LOCAL_FACTS_VARIANTS, LocalFacts, localFactsReady, type LocalFact, type LocalFactsPermit, type LocalFactsProps, type LocalFactsRepeatedSlot, type LocalFactsSkin, type LocalFactsSlot, type LocalFactsVariant, } from './local-facts.js';
|
|
@@ -25,4 +25,5 @@ export { BeforeAfter, beforeAfterReady, type BeforeAfterLayout, type BeforeAfter
|
|
|
25
25
|
export { LICENCES_VARIANTS, Licences, licencesContent, licencesReady, type LicencesBusiness, type LicencesLabels, type LicencesProps, type LicencesRepeatedSlot, type LicencesSkin, type LicencesSlot, type LicencesVariant, } from './licences.js';
|
|
26
26
|
export { QUOTE_CHECKLIST_MAX, QuoteChecklist, type QuoteChecklistLayout, type QuoteChecklistProps, type QuoteChecklistSkin, type QuoteChecklistSlot, } from './quote-checklist.js';
|
|
27
27
|
export { EmergencyBand, emergencyBandReady, renderableAfterHours, type EmergencyBandBusiness, type EmergencyBandLayout, type EmergencyBandProps, type EmergencyBandSkin, type EmergencyBandSlot, } from './emergency-band.js';
|
|
28
|
+
export { SignatureSection, type SignatureSectionLayout, type SignatureSectionProps, type SignatureSectionSkin, type SignatureSectionSlot, } from './signature-section.js';
|
|
28
29
|
//# 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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { hasGoogleRating, RatingChip } from './rating-chip.js';
|
|
1
|
+
export { hasGoogleRating, RatingChip, } from './rating-chip.js';
|
|
2
2
|
export { REVIEWS_BAND_HEADING_ID, REVIEWS_BAND_SKIP_REASON, REVIEWS_BAND_VARIANTS, ReviewsBand, ReviewsBandHeading, ReviewsBandItem, ReviewsBandList, ReviewsBandRoot, ReviewsBandSummary, reviewsBandReady, splitReviewText, } from './reviews-band.js';
|
|
3
3
|
export { CTA_BAND_HEADING_ID, CTA_BAND_VARIANTS, CTABand } from './cta-band.js';
|
|
4
4
|
export { SITE_FOOTER_VARIANTS, SiteFooter, } from './site-footer.js';
|
|
@@ -10,7 +10,7 @@ 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';
|
|
13
|
+
export { HERO_CHECKLIST_MAX, HERO_CONTENT_PARTS, HERO_FAMILIES, HERO_FORM_HEADING_ID, HERO_FORM_ID, HERO_JOB_MODES, HERO_SUMMARY_MAX_WORDS, HERO_SCRIMS, HERO_VARIANTS, Hero, heroContentOrder, } from './hero.js';
|
|
14
14
|
export { SERVICES_GRID_CARD_LIMIT, SERVICES_GRID_VARIANTS, ServiceCard, ServicesGrid, defaultImageSizes, } from './services-grid.js';
|
|
15
15
|
export { CouncilRecordLines, CouncilRules, councilFeeLine, councilRecordReady, councilRulesReady, formatCouncilFeeAmount, } from './council-rules.js';
|
|
16
16
|
export { LOCAL_FACTS_VARIANTS, LocalFacts, localFactsReady, } from './local-facts.js';
|
|
@@ -25,4 +25,5 @@ export { BeforeAfter, beforeAfterReady, } from './before-after.js';
|
|
|
25
25
|
export { LICENCES_VARIANTS, Licences, licencesContent, licencesReady, } from './licences.js';
|
|
26
26
|
export { QUOTE_CHECKLIST_MAX, QuoteChecklist, } from './quote-checklist.js';
|
|
27
27
|
export { EmergencyBand, emergencyBandReady, renderableAfterHours, } from './emergency-band.js';
|
|
28
|
+
export { SignatureSection, } from './signature-section.js';
|
|
28
29
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,UAAU,GAMX,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,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,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,WAAW,EACX,aAAa,EACb,IAAI,EACJ,gBAAgB,GAUjB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,WAAW,EACX,YAAY,EACZ,iBAAiB,GAUlB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,GAOvB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,GAQhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,GAOhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,WAAW,EACX,eAAe,EACf,gBAAgB,GAMjB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,iBAAiB,EACjB,OAAO,EACP,aAAa,EACb,YAAY,GAOb,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,iBAAiB,EACjB,QAAQ,EACR,gBAAgB,EAChB,aAAa,GASd,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EACf,oBAAoB,GAOrB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,mBAAmB,EACnB,QAAQ,EACR,kBAAkB,EAClB,aAAa,GAMd,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,OAAO,EACP,YAAY,GAOb,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,WAAW,EACX,gBAAgB,GAOjB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,iBAAiB,EACjB,QAAQ,EACR,eAAe,EACf,aAAa,GAQd,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,mBAAmB,EACnB,cAAc,GAKf,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,oBAAoB,GAMrB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,gBAAgB,GAKjB,MAAM,wBAAwB,CAAA","sourcesContent":["export {\n hasGoogleRating,\n RatingChip,\n type RatingChipData,\n type RatingChipLayout,\n type RatingChipProps,\n type RatingChipSkin,\n type RatingChipSlot,\n} from './rating-chip.js'\nexport {\n REVIEWS_BAND_HEADING_ID,\n REVIEWS_BAND_SKIP_REASON,\n REVIEWS_BAND_VARIANTS,\n ReviewsBand,\n ReviewsBandHeading,\n ReviewsBandItem,\n ReviewsBandList,\n ReviewsBandRoot,\n ReviewsBandSummary,\n reviewsBandReady,\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_CONTENT_PARTS,\n HERO_FAMILIES,\n HERO_FORM_HEADING_ID,\n HERO_FORM_ID,\n HERO_JOB_MODES,\n HERO_SUMMARY_MAX_WORDS,\n HERO_SCRIMS,\n HERO_VARIANTS,\n Hero,\n heroContentOrder,\n type HeroContentPart,\n type HeroFamily,\n type HeroJobMode,\n type HeroLayout,\n type HeroProps,\n type HeroScrim,\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'\nexport {\n CouncilRecordLines,\n CouncilRules,\n councilFeeLine,\n councilRecordReady,\n councilRulesReady,\n formatCouncilFeeAmount,\n type CouncilRecordSlot,\n type CouncilRuleWording,\n type CouncilRulesProps,\n type CouncilRulesRepeatedSlot,\n type CouncilRulesSkin,\n type CouncilRulesSlot,\n} from './council-rules.js'\nexport {\n LOCAL_FACTS_VARIANTS,\n LocalFacts,\n localFactsReady,\n type LocalFact,\n type LocalFactsPermit,\n type LocalFactsProps,\n type LocalFactsRepeatedSlot,\n type LocalFactsSkin,\n type LocalFactsSlot,\n type LocalFactsVariant,\n} from './local-facts.js'\nexport {\n NEARBY_JOBS_VARIANTS,\n NearbyJobs,\n nearbyJobsReady,\n type NearbyJobsLayout,\n type NearbyJobsProps,\n type NearbyJobsRepeatedSlot,\n type NearbyJobsSkin,\n type NearbyJobsSlot,\n type NearbyJobsVariant,\n} from './nearby-jobs.js'\nexport {\n NEARBY_AREAS_DEFAULT,\n NEARBY_AREAS_LIMIT,\n NEARBY_AREAS_VARIANTS,\n NearbyAreas,\n nearbyAreaLinks,\n nearbyAreasReady,\n type NearbyAreasLayout,\n type NearbyAreasProps,\n type NearbyAreasSkin,\n type NearbyAreasSlot,\n type NearbyAreasVariant,\n} from './nearby-areas.js'\nexport {\n AREA_MAP_VARIANTS,\n AreaMap,\n areaMapPlaces,\n areaMapReady,\n type AreaMapData,\n type AreaMapLayout,\n type AreaMapProps,\n type AreaMapSkin,\n type AreaMapSlot,\n type AreaMapVariant,\n} from './area-map.js'\nexport {\n COVERAGE_VARIANTS,\n Coverage,\n coverageHubLabel,\n coverageReady,\n type CoverageGroup,\n type CoverageHub,\n type CoverageLayout,\n type CoverageProps,\n type CoverageRepeatedSlot,\n type CoverageSkin,\n type CoverageSlot,\n type CoverageVariant,\n} from './coverage.js'\nexport {\n RELATED_SERVICES_LIMIT,\n RELATED_SERVICES_VARIANTS,\n RelatedServices,\n relatedServicesReady,\n type RelatedServicesLayout,\n type RelatedServicesProps,\n type RelatedServicesRepeatedSlot,\n type RelatedServicesSkin,\n type RelatedServicesSlot,\n type RelatedServicesVariant,\n} from './related-services.js'\nexport {\n PROJECTS_CARD_LIMIT,\n Projects,\n projectCardFromJob,\n projectsReady,\n type ProjectCardData,\n type ProjectsLayout,\n type ProjectsProps,\n type ProjectsSkin,\n type ProjectsSlot,\n} from './projects.js'\nexport {\n Gallery,\n galleryReady,\n type GalleryLayout,\n type GalleryPhoto,\n type GalleryProps,\n type GalleryRepeatedSlot,\n type GallerySkin,\n type GallerySlot,\n} from './gallery.js'\nexport {\n BeforeAfter,\n beforeAfterReady,\n type BeforeAfterLayout,\n type BeforeAfterPair,\n type BeforeAfterProps,\n type BeforeAfterRepeatedSlot,\n type BeforeAfterSkin,\n type BeforeAfterSlot,\n} from './before-after.js'\nexport {\n LICENCES_VARIANTS,\n Licences,\n licencesContent,\n licencesReady,\n type LicencesBusiness,\n type LicencesLabels,\n type LicencesProps,\n type LicencesRepeatedSlot,\n type LicencesSkin,\n type LicencesSlot,\n type LicencesVariant,\n} from './licences.js'\nexport {\n QUOTE_CHECKLIST_MAX,\n QuoteChecklist,\n type QuoteChecklistLayout,\n type QuoteChecklistProps,\n type QuoteChecklistSkin,\n type QuoteChecklistSlot,\n} from './quote-checklist.js'\nexport {\n EmergencyBand,\n emergencyBandReady,\n renderableAfterHours,\n type EmergencyBandBusiness,\n type EmergencyBandLayout,\n type EmergencyBandProps,\n type EmergencyBandSkin,\n type EmergencyBandSlot,\n} from './emergency-band.js'\nexport {\n SignatureSection,\n type SignatureSectionLayout,\n type SignatureSectionProps,\n type SignatureSectionSkin,\n type SignatureSectionSlot,\n} from './signature-section.js'\n"]}
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* already verified (`verified` or `legacy-carried` facts, T4); it never counts up or hides text before scripts run.
|
|
5
5
|
* With fewer than three items it renders nothing and reports `merge-into-hero-checklist`: the facts belong in the
|
|
6
6
|
* hero's checklist instead (v1 §7.2).
|
|
7
|
+
*
|
|
8
|
+
* 3.16.0: an item with `href` links its value and label to where the fact can be checked (the reviews, the licence
|
|
9
|
+
* register, the about page), never prefetched; external links open in a new tab. The `tiles` variant sets each value
|
|
10
|
+
* as a large figure (`--bl-proof-figure-size`) over its label, as AA's home strip does.
|
|
7
11
|
*/
|
|
8
12
|
import type { ReactNode } from 'react';
|
|
9
13
|
import { type BrandlabSectionReadiness } from '@brandlab-digital/site-core/section-order';
|
|
@@ -13,7 +17,7 @@ import type { RepeatedSlotOf } from '../repeated-slots.js';
|
|
|
13
17
|
import { type SkinEntry } from '../skin.js';
|
|
14
18
|
export declare const PROOF_STRIP_VARIANTS: readonly ["inline", "tiles"];
|
|
15
19
|
export type ProofStripVariant = (typeof PROOF_STRIP_VARIANTS)[number];
|
|
16
|
-
export type ProofStripSlot = SectionSlot | 'heading' | 'list' | 'item' | 'icon' | 'stars' | 'value' | 'label' | 'logos';
|
|
20
|
+
export type ProofStripSlot = SectionSlot | 'heading' | 'list' | 'item' | 'link' | 'icon' | 'stars' | 'value' | 'label' | 'logos';
|
|
17
21
|
/** `heading`: the section's H2 is visually hidden by default; a skin may show it. */
|
|
18
22
|
/** Rendered once per proof item: styled from the skin stylesheet, never by class (3.14.0). */
|
|
19
23
|
export type ProofStripRepeatedSlot = RepeatedSlotOf<'proofStrip'>;
|
|
@@ -27,6 +31,8 @@ interface ProofItemBase {
|
|
|
27
31
|
readonly id: string;
|
|
28
32
|
readonly label: string;
|
|
29
33
|
readonly icon?: UiIconName;
|
|
34
|
+
/** Where the fact can be checked (3.16.0), for example `/reviews` or the licence register. */
|
|
35
|
+
readonly href?: string;
|
|
30
36
|
}
|
|
31
37
|
/** One verified fact, for example `{ id: 'licence', value: '276621C', label: 'NSW contractor licence' }`. */
|
|
32
38
|
export interface ProofFactItem extends ProofItemBase {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof-strip.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/proof-strip.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"proof-strip.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/proof-strip.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,EAAiB,KAAK,wBAAwB,EAAE,MAAM,2CAA2C,CAAA;AACxG,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAEnD,OAAO,EAAwB,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAClG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AAE/D,eAAO,MAAM,oBAAoB,8BAA+B,CAAA;AAChE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AACrE,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAA;AAChI,qFAAqF;AACrF,8FAA8F;AAC9F,MAAM,MAAM,sBAAsB,GAAG,cAAc,CAAC,YAAY,CAAC,CAAA;AACjE,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC,cAAc,EAAE,iBAAiB,EAAE;IAAE,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;CAAE,EAAE,sBAAsB,CAAC,CAAA;AAErI,0FAA0F;AAC1F,eAAO,MAAM,mBAAmB,IAAI,CAAA;AAEpC,UAAU,aAAa;IACrB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAA;IAC1B,8FAA8F;IAC9F,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,6GAA6G;AAC7G,MAAM,WAAW,aAAc,SAAQ,aAAa;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,CAAA;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CACvB;AAED,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,eAAe,CAAA;AAEvD,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,mGAAmG;IACnG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAA;IACpC,mFAAmF;IACnF,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAA;IAC1B,mCAAmC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,gCAAgC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAA;CAC5B;AAED,4GAA4G;AAC5G,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,wBAAwB,CAGpH;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAA2B,EAAE,IAAI,EAAE,OAAkB,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,eAAe,kDAgD1I"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { SECTION_READY } from '@brandlab-digital/site-core/section-order';
|
|
3
3
|
import { Icon } from '../icons.js';
|
|
4
|
+
import { Anchor } from '../primitives/links.js';
|
|
4
5
|
import { RatingStars, Section } from '../primitives/layout.js';
|
|
5
6
|
import { repeatedSlot, slot } from '../skin.js';
|
|
6
7
|
export const PROOF_STRIP_VARIANTS = ['inline', 'tiles'];
|
|
@@ -15,6 +16,9 @@ export function ProofStrip({ title, items, logos, headingId = 'proof-heading', t
|
|
|
15
16
|
if (!proofStripReady({ items, logos }).render)
|
|
16
17
|
return null;
|
|
17
18
|
const hidden = (layout?.heading ?? 'hidden') === 'hidden';
|
|
18
|
-
return (_jsxs(Section, { id: "proof-strip", labelledBy: headingId, tone: tone, variant: variant, classes: classes, children: [_jsx("h2", { id: headingId, ...slot(classes, 'heading'), "data-bl-visually-hidden": hidden ? '' : undefined, children: title }), items.length > 0 ? (_jsx("ul", { role: "list", ...slot(classes, 'list'), children: items.map((item) =>
|
|
19
|
+
return (_jsxs(Section, { id: "proof-strip", labelledBy: headingId, tone: tone, variant: variant, classes: classes, children: [_jsx("h2", { id: headingId, ...slot(classes, 'heading'), "data-bl-visually-hidden": hidden ? '' : undefined, children: title }), items.length > 0 ? (_jsx("ul", { role: "list", ...slot(classes, 'list'), children: items.map((item) => {
|
|
20
|
+
const content = (_jsxs(_Fragment, { children: [item.icon ? (_jsx("span", { ...repeatedSlot('icon'), children: _jsx(Icon, { name: item.icon }) })) : null, item.rating !== undefined ? (_jsx("span", { ...repeatedSlot('stars'), "aria-hidden": "true", children: _jsx(RatingStars, { rating: item.rating }) })) : null, item.rating !== undefined ? (_jsx("span", { ...repeatedSlot('value'), children: item.rating.toFixed(1) })) : item.value ? (_jsx("span", { ...repeatedSlot('value'), children: item.value })) : null, ' ', _jsx("span", { ...repeatedSlot('label'), children: item.label })] }));
|
|
21
|
+
return (_jsx("li", { ...repeatedSlot('item'), "data-item": item.id, "data-bl-source": item.rating !== undefined ? 'feed' : undefined, children: item.href ? (_jsx(Anchor, { href: item.href, prefetch: false, hooks: repeatedSlot('link'), children: content })) : (content) }, item.id));
|
|
22
|
+
}) })) : null, logos ? _jsx("div", { ...slot(classes, 'logos'), children: logos }) : null] }));
|
|
19
23
|
}
|
|
20
24
|
//# sourceMappingURL=proof-strip.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof-strip.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/proof-strip.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"proof-strip.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/proof-strip.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * `ProofStrip` (S3, `proof-strip`): the business's verified strengths straight after the hero: the live rating from\n * the feed, licence lines, insurance, years trading and partner logos. Server component. It takes items that are\n * already verified (`verified` or `legacy-carried` facts, T4); it never counts up or hides text before scripts run.\n * With fewer than three items it renders nothing and reports `merge-into-hero-checklist`: the facts belong in the\n * hero's checklist instead (v1 §7.2).\n *\n * 3.16.0: an item with `href` links its value and label to where the fact can be checked (the reviews, the licence\n * register, the about page), never prefetched; external links open in a new tab. The `tiles` variant sets each value\n * as a large figure (`--bl-proof-figure-size`) over its label, as AA's home strip does.\n */\nimport type { ReactNode } from 'react'\nimport { SECTION_READY, type BrandlabSectionReadiness } from '@brandlab-digital/site-core/section-order'\nimport { Icon, type UiIconName } from '../icons.js'\nimport { Anchor } from '../primitives/links.js'\nimport { RatingStars, Section, type SectionSlot, type SectionTone } from '../primitives/layout.js'\nimport type { RepeatedSlotOf } from '../repeated-slots.js'\nimport { repeatedSlot, slot, type SkinEntry } from '../skin.js'\n\nexport const PROOF_STRIP_VARIANTS = ['inline', 'tiles'] as const\nexport type ProofStripVariant = (typeof PROOF_STRIP_VARIANTS)[number]\nexport type ProofStripSlot = SectionSlot | 'heading' | 'list' | 'item' | 'link' | 'icon' | 'stars' | 'value' | 'label' | 'logos'\n/** `heading`: the section's H2 is visually hidden by default; a skin may show it. */\n/** Rendered once per proof item: styled from the skin stylesheet, never by class (3.14.0). */\nexport type ProofStripRepeatedSlot = RepeatedSlotOf<'proofStrip'>\nexport type ProofStripSkin = SkinEntry<ProofStripSlot, ProofStripVariant, { heading?: 'hidden' | 'visible' }, ProofStripRepeatedSlot>\n\n/** The fewest items a strip shows; below this the facts merge into the hero checklist. */\nexport const PROOF_STRIP_MINIMUM = 3\n\ninterface ProofItemBase {\n /** A stable slug, rendered as `data-item`. */\n readonly id: string\n readonly label: string\n readonly icon?: UiIconName\n /** Where the fact can be checked (3.16.0), for example `/reviews` or the licence register. */\n readonly href?: string\n}\n\n/** One verified fact, for example `{ id: 'licence', value: '276621C', label: 'NSW contractor licence' }`. */\nexport interface ProofFactItem extends ProofItemBase {\n readonly value?: string\n readonly rating?: never\n}\n\n/**\n * The live rating from the reviews feed (F9), never typed by hand (T4). Its value is the rating itself (\"4.9\"), with\n * the stars beside it, and the item is marked `data-bl-source=\"feed\"`, as the reviews band marks its root.\n */\nexport interface ProofRatingItem extends ProofItemBase {\n readonly rating: number\n readonly value?: never\n}\n\nexport type ProofItem = ProofFactItem | ProofRatingItem\n\nexport interface ProofStripProps extends ProofStripSkin {\n /** The H2, for example \"Why homeowners choose Omega\". Visually hidden unless the skin shows it. */\n readonly title: string\n readonly items: readonly ProofItem[]\n /** Partner logos (a `LogoStrip` of verified relationships). Counts as one item. */\n readonly logos?: ReactNode\n /** Defaults to `proof-heading`. */\n readonly headingId?: string\n /** Set by `SectionSequence`. */\n readonly tone?: SectionTone\n}\n\n/** Whether the strip renders: at least three items, logos counting as one (`merge-into-hero-checklist`). */\nexport function proofStripReady({ items, logos }: Pick<ProofStripProps, 'items' | 'logos'>): BrandlabSectionReadiness {\n const count = items.length + (logos ? 1 : 0)\n return count >= PROOF_STRIP_MINIMUM ? SECTION_READY : { render: false, reason: 'merge-into-hero-checklist' }\n}\n\nexport function ProofStrip({ title, items, logos, headingId = 'proof-heading', tone, variant = 'inline', layout, classes }: ProofStripProps) {\n if (!proofStripReady({ items, logos }).render) return null\n const hidden = (layout?.heading ?? 'hidden') === 'hidden'\n return (\n <Section id=\"proof-strip\" labelledBy={headingId} tone={tone} variant={variant} classes={classes}>\n <h2 id={headingId} {...slot(classes, 'heading')} data-bl-visually-hidden={hidden ? '' : undefined}>\n {title}\n </h2>\n {items.length > 0 ? (\n <ul role=\"list\" {...slot(classes, 'list')}>\n {items.map((item) => {\n const content = (\n <>\n {item.icon ? (\n <span {...repeatedSlot('icon')}>\n <Icon name={item.icon} />\n </span>\n ) : null}\n {item.rating !== undefined ? (\n <span {...repeatedSlot('stars')} aria-hidden=\"true\">\n <RatingStars rating={item.rating} />\n </span>\n ) : null}\n {item.rating !== undefined ? (\n <span {...repeatedSlot('value')}>{item.rating.toFixed(1)}</span>\n ) : item.value ? (\n <span {...repeatedSlot('value')}>{item.value}</span>\n ) : null}{' '}\n <span {...repeatedSlot('label')}>{item.label}</span>\n </>\n )\n return (\n <li key={item.id} {...repeatedSlot('item')} data-item={item.id} data-bl-source={item.rating !== undefined ? 'feed' : undefined}>\n {item.href ? (\n <Anchor href={item.href} prefetch={false} hooks={repeatedSlot('link')}>\n {content}\n </Anchor>\n ) : (\n content\n )}\n </li>\n )\n })}\n </ul>\n ) : null}\n {logos ? <div {...slot(classes, 'logos')}>{logos}</div> : null}\n </Section>\n )\n}\n"]}
|
|
@@ -3,23 +3,33 @@
|
|
|
3
3
|
* and a link to the Business Profile. Server component. The numbers come only from the reviews feed (T4), never typed
|
|
4
4
|
* by hand; without both an average and a total of 1 or more it renders nothing. `data-bl-source="feed"` marks the
|
|
5
5
|
* numbers as the feed's for the ready gate (T4, T5).
|
|
6
|
+
*
|
|
7
|
+
* 3.16.0: `layout.display: 'figure'` sets the average as a large figure ("4.9", with "out of 5" for assistive
|
|
8
|
+
* technology) followed by the stars and the count, as AA's hero shows it under the H1; `moreLabel` adds the site's own
|
|
9
|
+
* link to the Business Profile ("Read all reviews on Google"), and the count becomes plain text so the profile is
|
|
10
|
+
* linked once.
|
|
6
11
|
*/
|
|
7
12
|
import type { PreparedBusinessProfileReviews } from '@brandlab-digital/site-core/google-business-reviews';
|
|
8
13
|
import { type SkinEntry } from '../skin.js';
|
|
9
|
-
export type RatingChipSlot = 'root' | 'stars' | 'rating' | 'source';
|
|
10
|
-
export
|
|
11
|
-
stars?: boolean;
|
|
12
|
-
|
|
14
|
+
export type RatingChipSlot = 'root' | 'stars' | 'rating' | 'figure' | 'source' | 'more';
|
|
15
|
+
export interface RatingChipLayout {
|
|
16
|
+
readonly stars?: boolean;
|
|
17
|
+
/** `inline` (the default): one line of words; `figure`: the average set large, then the stars and the count. */
|
|
18
|
+
readonly display?: 'inline' | 'figure';
|
|
19
|
+
}
|
|
20
|
+
export type RatingChipSkin = SkinEntry<RatingChipSlot, never, RatingChipLayout>;
|
|
13
21
|
/** The part of the prepared reviews the chip reads: Google's average, total and profile link. */
|
|
14
22
|
export type RatingChipData = Pick<PreparedBusinessProfileReviews, 'averageRating' | 'totalReviewCount' | 'profileUrl'>;
|
|
15
23
|
export interface RatingChipProps extends RatingChipSkin {
|
|
16
24
|
/** From `prepareBusinessProfileReviews`; null renders nothing. */
|
|
17
25
|
readonly reviews: RatingChipData | null | undefined;
|
|
26
|
+
/** The site's words for a link to the Business Profile, for example "Read all reviews on Google". */
|
|
27
|
+
readonly moreLabel?: string | undefined;
|
|
18
28
|
}
|
|
19
29
|
/** Whether the feed gave Google's average and a total of 1 or more, so a rating can be shown. */
|
|
20
30
|
export declare function hasGoogleRating(reviews: RatingChipData | null | undefined): reviews is RatingChipData & {
|
|
21
31
|
averageRating: number;
|
|
22
32
|
totalReviewCount: number;
|
|
23
33
|
};
|
|
24
|
-
export declare function RatingChip({ reviews, layout, classes }: RatingChipProps): import("react/jsx-runtime").JSX.Element | null;
|
|
34
|
+
export declare function RatingChip({ reviews, moreLabel, layout, classes }: RatingChipProps): import("react/jsx-runtime").JSX.Element | null;
|
|
25
35
|
//# sourceMappingURL=rating-chip.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rating-chip.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/rating-chip.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rating-chip.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/rating-chip.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,qDAAqD,CAAA;AAEzG,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AAEjD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAA;AACvF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;IACxB,gHAAgH;IAChH,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAA;CACvC;AACD,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC,cAAc,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAA;AAE/E,iGAAiG;AACjG,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,8BAA8B,EAAE,eAAe,GAAG,kBAAkB,GAAG,YAAY,CAAC,CAAA;AAEtH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,CAAA;IACnD,qGAAqG;IACrG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACxC;AAID,iGAAiG;AACjG,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,IAAI,cAAc,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,CAE3J;AAED,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,eAAe,kDAqClF"}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { RatingStars } from '../primitives/layout.js';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { RatingStars, VisuallyHidden } from '../primitives/layout.js';
|
|
3
3
|
import { slot } from '../skin.js';
|
|
4
4
|
const COUNT = new Intl.NumberFormat('en-AU');
|
|
5
5
|
/** Whether the feed gave Google's average and a total of 1 or more, so a rating can be shown. */
|
|
6
6
|
export function hasGoogleRating(reviews) {
|
|
7
7
|
return typeof reviews?.averageRating === 'number' && typeof reviews.totalReviewCount === 'number' && reviews.totalReviewCount > 0;
|
|
8
8
|
}
|
|
9
|
-
export function RatingChip({ reviews, layout, classes }) {
|
|
9
|
+
export function RatingChip({ reviews, moreLabel, layout, classes }) {
|
|
10
10
|
if (!hasGoogleRating(reviews))
|
|
11
11
|
return null;
|
|
12
12
|
const total = reviews.totalReviewCount;
|
|
13
|
-
|
|
13
|
+
const figure = layout?.display === 'figure';
|
|
14
|
+
const average = reviews.averageRating.toFixed(1);
|
|
15
|
+
const stars = layout?.stars === false ? null : (_jsx("span", { ...slot(classes, 'stars'), "aria-hidden": "true", children: _jsx(RatingStars, { rating: reviews.averageRating }) }));
|
|
16
|
+
const count = `${COUNT.format(total)} Google ${total === 1 ? 'review' : 'reviews'}`;
|
|
17
|
+
const more = moreLabel?.trim();
|
|
18
|
+
const link = { href: reviews.profileUrl, target: '_blank', rel: 'noopener noreferrer' };
|
|
19
|
+
return (_jsxs("p", { "data-bl": "rating-chip", "data-bl-source": "feed", "data-display": figure ? 'figure' : undefined, className: classes?.root, children: [figure ? null : stars, figure ? (_jsxs("span", { ...slot(classes, 'rating'), children: [_jsx("span", { ...slot(classes, 'figure'), children: average }), _jsx(VisuallyHidden, { children: " out of 5" })] })) : (_jsxs("span", { ...slot(classes, 'rating'), children: [average, " out of 5"] })), figure ? stars : null, " from", ' ', more ? _jsx("span", { ...slot(classes, 'source'), children: count }) : _jsx("a", { ...slot(classes, 'source'), ...link, children: count }), more ? (_jsxs(_Fragment, { children: [' ', _jsx("a", { ...slot(classes, 'more'), ...link, children: more })] })) : null] }));
|
|
14
20
|
}
|
|
15
21
|
//# sourceMappingURL=rating-chip.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rating-chip.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/rating-chip.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"rating-chip.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/rating-chip.tsx"],"names":[],"mappings":";AAYA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAE,IAAI,EAAkB,MAAM,YAAY,CAAA;AAoBjD,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;AAE5C,iGAAiG;AACjG,MAAM,UAAU,eAAe,CAAC,OAA0C;IACxE,OAAO,OAAO,OAAO,EAAE,aAAa,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,gBAAgB,KAAK,QAAQ,IAAI,OAAO,CAAC,gBAAgB,GAAG,CAAC,CAAA;AACnI,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAmB;IACjF,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAA;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAA;IACtC,MAAM,MAAM,GAAG,MAAM,EAAE,OAAO,KAAK,QAAQ,CAAA;IAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAChD,MAAM,KAAK,GACT,MAAM,EAAE,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAC/B,kBAAU,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,iBAAc,MAAM,YAClD,KAAC,WAAW,IAAC,MAAM,EAAE,OAAO,CAAC,aAAa,GAAI,GACzC,CACR,CAAA;IACH,MAAM,KAAK,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,CAAA;IACnF,MAAM,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,CAAA;IAC9B,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,qBAAqB,EAAW,CAAA;IAChG,OAAO,CACL,wBAAW,aAAa,oBAAgB,MAAM,kBAAe,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,aACjH,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EACrB,MAAM,CAAC,CAAC,CAAC,CACR,mBAAU,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,aAC/B,kBAAU,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAG,OAAO,GAAQ,EACnD,KAAC,cAAc,4BAA2B,IACrC,CACR,CAAC,CAAC,CAAC,CACF,mBAAU,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,aAAG,OAAO,iBAAiB,CAC7D,EACA,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,WAAO,GAAG,EAC/B,IAAI,CAAC,CAAC,CAAC,kBAAU,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAG,KAAK,GAAQ,CAAC,CAAC,CAAC,eAAO,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAM,IAAI,YAAG,KAAK,GAAK,EAChH,IAAI,CAAC,CAAC,CAAC,CACN,8BACG,GAAG,EACJ,eAAO,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,KAAM,IAAI,YACnC,IAAI,GACH,IACH,CACJ,CAAC,CAAC,CAAC,IAAI,IACN,CACL,CAAA;AACH,CAAC","sourcesContent":["/**\n * `RatingChip`: Google's own average and total in words (\"4.9 out of 5 from 99 Google reviews\"), with decorative stars\n * and a link to the Business Profile. Server component. The numbers come only from the reviews feed (T4), never typed\n * by hand; without both an average and a total of 1 or more it renders nothing. `data-bl-source=\"feed\"` marks the\n * numbers as the feed's for the ready gate (T4, T5).\n *\n * 3.16.0: `layout.display: 'figure'` sets the average as a large figure (\"4.9\", with \"out of 5\" for assistive\n * technology) followed by the stars and the count, as AA's hero shows it under the H1; `moreLabel` adds the site's own\n * link to the Business Profile (\"Read all reviews on Google\"), and the count becomes plain text so the profile is\n * linked once.\n */\nimport type { PreparedBusinessProfileReviews } from '@brandlab-digital/site-core/google-business-reviews'\nimport { RatingStars, VisuallyHidden } from '../primitives/layout.js'\nimport { slot, type SkinEntry } from '../skin.js'\n\nexport type RatingChipSlot = 'root' | 'stars' | 'rating' | 'figure' | 'source' | 'more'\nexport interface RatingChipLayout {\n readonly stars?: boolean\n /** `inline` (the default): one line of words; `figure`: the average set large, then the stars and the count. */\n readonly display?: 'inline' | 'figure'\n}\nexport type RatingChipSkin = SkinEntry<RatingChipSlot, never, RatingChipLayout>\n\n/** The part of the prepared reviews the chip reads: Google's average, total and profile link. */\nexport type RatingChipData = Pick<PreparedBusinessProfileReviews, 'averageRating' | 'totalReviewCount' | 'profileUrl'>\n\nexport interface RatingChipProps extends RatingChipSkin {\n /** From `prepareBusinessProfileReviews`; null renders nothing. */\n readonly reviews: RatingChipData | null | undefined\n /** The site's words for a link to the Business Profile, for example \"Read all reviews on Google\". */\n readonly moreLabel?: string | undefined\n}\n\nconst COUNT = new Intl.NumberFormat('en-AU')\n\n/** Whether the feed gave Google's average and a total of 1 or more, so a rating can be shown. */\nexport function hasGoogleRating(reviews: RatingChipData | null | undefined): reviews is RatingChipData & { averageRating: number; totalReviewCount: number } {\n return typeof reviews?.averageRating === 'number' && typeof reviews.totalReviewCount === 'number' && reviews.totalReviewCount > 0\n}\n\nexport function RatingChip({ reviews, moreLabel, layout, classes }: RatingChipProps) {\n if (!hasGoogleRating(reviews)) return null\n const total = reviews.totalReviewCount\n const figure = layout?.display === 'figure'\n const average = reviews.averageRating.toFixed(1)\n const stars =\n layout?.stars === false ? null : (\n <span {...slot(classes, 'stars')} aria-hidden=\"true\">\n <RatingStars rating={reviews.averageRating} />\n </span>\n )\n const count = `${COUNT.format(total)} Google ${total === 1 ? 'review' : 'reviews'}`\n const more = moreLabel?.trim()\n const link = { href: reviews.profileUrl, target: '_blank', rel: 'noopener noreferrer' } as const\n return (\n <p data-bl=\"rating-chip\" data-bl-source=\"feed\" data-display={figure ? 'figure' : undefined} className={classes?.root}>\n {figure ? null : stars}\n {figure ? (\n <span {...slot(classes, 'rating')}>\n <span {...slot(classes, 'figure')}>{average}</span>\n <VisuallyHidden> out of 5</VisuallyHidden>\n </span>\n ) : (\n <span {...slot(classes, 'rating')}>{average} out of 5</span>\n )}\n {figure ? stars : null} from{' '}\n {more ? <span {...slot(classes, 'source')}>{count}</span> : <a {...slot(classes, 'source')} {...link}>{count}</a>}\n {more ? (\n <>\n {' '}\n <a {...slot(classes, 'more')} {...link}>\n {more}\n </a>\n </>\n ) : null}\n </p>\n )\n}\n"]}
|