@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
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* `ServicesGrid` (S4, `services`, 3.14.0) and `ServiceCard`: route the visitor to the right service. Server components.
|
|
4
|
+
*
|
|
5
|
+
* Variants:
|
|
6
|
+
* - `rows` (the fleet reference, AA's `ServiceRows`): one full-width row per service, the photo beside the text and
|
|
7
|
+
* alternating sides on wide screens, with an optional line of scope points and the service's own link;
|
|
8
|
+
* - `cards`: a grid of cards, each headed by its linked title;
|
|
9
|
+
* - `compact` (suburb and region pages): a lean list of every service offered there, each a single link to its
|
|
10
|
+
* service-in-suburb page or to its anchored section on the same page (`#tree-pruning`). No photos and no summaries.
|
|
11
|
+
*
|
|
12
|
+
* Locked rules (S4): one link per card; no prices and no before-and-afters in cards; no image on two cards; at most 8
|
|
13
|
+
* `rows` or `cards` (K11's card limit, and the most in-content links that prefetch on intent), so a page that offers
|
|
14
|
+
* more services lists them `compact` or links its services hub. Card links prefetch on intent (`LINK_PREFETCH_POLICY`
|
|
15
|
+
* `contentCard`); compact links, the hub link and same-page anchors never prefetch.
|
|
16
|
+
*/
|
|
17
|
+
import Link from 'next/link';
|
|
18
|
+
import { LINK_PREFETCH_LIMITS, LINK_PREFETCH_POLICY, linkPrefetchProps } from '../prefetch.js';
|
|
19
|
+
import { Media } from '../primitives/media.js';
|
|
20
|
+
import { Section, SectionHeading } from '../primitives/layout.js';
|
|
21
|
+
import { repeatedSlot, slot } from '../skin.js';
|
|
22
|
+
export const SERVICES_GRID_VARIANTS = ['rows', 'cards', 'compact'];
|
|
23
|
+
/** The most services `rows` and `cards` show (K11; the in-content intent-prefetch limit). */
|
|
24
|
+
export const SERVICES_GRID_CARD_LIMIT = LINK_PREFETCH_LIMITS.contentCard;
|
|
25
|
+
function ServiceLink({ href, prefetch, children }) {
|
|
26
|
+
// A same-page section needs no route prefetch: a plain anchor.
|
|
27
|
+
if (href.startsWith('#')) {
|
|
28
|
+
return (_jsx("a", { href: href, ...repeatedSlot('link'), children: children }));
|
|
29
|
+
}
|
|
30
|
+
return (_jsx(Link, { href: href, ...linkPrefetchProps(prefetch), ...repeatedSlot('link'), children: children }));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* One service as a list item of `ServicesGrid` (also the building block for `RelatedServices`). Its parts are the
|
|
34
|
+
* grid's repeated slots: the root sets no classes on them.
|
|
35
|
+
*/
|
|
36
|
+
export function ServiceCard({ service, variant, imageSizes, ratio = '4:3', prefetch }) {
|
|
37
|
+
if (variant === 'compact') {
|
|
38
|
+
return (_jsx("li", { ...repeatedSlot('item'), "data-item": service.id, children: _jsx(ServiceLink, { href: service.href, prefetch: LINK_PREFETCH_POLICY.otherServicesInSuburb, children: service.title }) }));
|
|
39
|
+
}
|
|
40
|
+
const linkPrefetch = prefetch ?? LINK_PREFETCH_POLICY.contentCard;
|
|
41
|
+
const separateLink = variant === 'rows' && service.linkLabel !== undefined && service.linkLabel.trim() !== '';
|
|
42
|
+
return (_jsxs("li", { ...repeatedSlot('item'), "data-item": service.id, children: [_jsxs("div", { ...repeatedSlot('body'), children: [_jsx("h3", { ...repeatedSlot('title'), children: separateLink ? (service.title) : (_jsx(ServiceLink, { href: service.href, prefetch: linkPrefetch, children: service.title })) }), service.summary ? _jsx("p", { ...repeatedSlot('summary'), children: service.summary }) : null, variant === 'rows' && service.points && service.points.length > 0 ? (_jsx("ul", { role: "list", ...repeatedSlot('points'), children: service.points.map((point) => (_jsx("li", { ...repeatedSlot('point'), children: point }, point))) })) : null, separateLink ? (_jsx("p", { ...repeatedSlot('action'), children: _jsx(ServiceLink, { href: service.href, prefetch: linkPrefetch, children: service.linkLabel }) })) : null] }), service.image ? (_jsx("div", { ...repeatedSlot('media'), children: _jsx(Media, { media: service.image, sizes: imageSizes ?? defaultImageSizes(variant, 3), layout: { ratio } }) })) : null] }));
|
|
43
|
+
}
|
|
44
|
+
/** The photo `sizes` for each variant: half the container in rows, one column of the grid in cards. */
|
|
45
|
+
export function defaultImageSizes(variant, columns) {
|
|
46
|
+
if (variant === 'rows')
|
|
47
|
+
return '(min-width: 1024px) 50vw, 100vw';
|
|
48
|
+
return `(min-width: 1024px) ${Math.round(100 / columns)}vw, (min-width: 640px) 50vw, 100vw`;
|
|
49
|
+
}
|
|
50
|
+
function assertServices(services, variant) {
|
|
51
|
+
const ids = new Set();
|
|
52
|
+
const hrefs = new Set();
|
|
53
|
+
const images = new Map();
|
|
54
|
+
for (const service of services) {
|
|
55
|
+
if (!service.id || ids.has(service.id))
|
|
56
|
+
throw new Error(`ServicesGrid: every service needs its own id ("${service.id}" is empty or repeated).`);
|
|
57
|
+
ids.add(service.id);
|
|
58
|
+
if (!service.title?.trim())
|
|
59
|
+
throw new Error(`ServicesGrid: the service "${service.id}" has no title.`);
|
|
60
|
+
if (!service.href?.trim())
|
|
61
|
+
throw new Error(`ServicesGrid: the service "${service.id}" has no link.`);
|
|
62
|
+
if (hrefs.has(service.href))
|
|
63
|
+
throw new Error(`ServicesGrid: two services link to ${service.href}; each card has its own page or section.`);
|
|
64
|
+
hrefs.add(service.href);
|
|
65
|
+
if (variant !== 'compact' && service.image) {
|
|
66
|
+
const other = images.get(service.image.src);
|
|
67
|
+
if (other)
|
|
68
|
+
throw new Error(`ServicesGrid: "${service.id}" and "${other}" use the same image (${service.image.src}); no image is on two cards.`);
|
|
69
|
+
images.set(service.image.src, service.id);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (variant !== 'compact' && services.length > SERVICES_GRID_CARD_LIMIT) {
|
|
73
|
+
throw new Error(`ServicesGrid: ${services.length} services in ${variant}; ${variant} shows at most ${SERVICES_GRID_CARD_LIMIT} (K11). List every service with the compact variant, or show the money services and link the services hub.`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function ServicesGrid({ title, eyebrow, lead, services, more, imageSizes, headingId = 'services-heading', tone, variant = 'rows', layout, classes, }) {
|
|
77
|
+
if (services.length === 0)
|
|
78
|
+
return null;
|
|
79
|
+
assertServices(services, variant);
|
|
80
|
+
const columns = layout?.columns ?? 3;
|
|
81
|
+
const ratio = layout?.ratio ?? '4:3';
|
|
82
|
+
const hooks = variant === 'compact'
|
|
83
|
+
? undefined
|
|
84
|
+
: variant === 'cards'
|
|
85
|
+
? { 'data-columns': String(columns), 'data-ratio': ratio }
|
|
86
|
+
: { 'data-media-side': layout?.mediaSide ?? 'alternate', 'data-ratio': ratio };
|
|
87
|
+
const sizes = variant === 'compact' ? undefined : (imageSizes ?? defaultImageSizes(variant, columns));
|
|
88
|
+
return (_jsxs(Section, { id: "services", labelledBy: headingId, tone: tone, variant: variant, classes: classes, hooks: hooks, children: [_jsx(SectionHeading, { id: headingId, title: title, eyebrow: eyebrow, lead: lead, classes: classes }), _jsx("ul", { role: "list", ...slot(classes, 'list'), children: services.map((service) => (_jsx(ServiceCard, { service: service, variant: variant, imageSizes: sizes, ratio: ratio }, service.id))) }), more ? (_jsx("p", { ...slot(classes, 'more'), children: more.href.startsWith('#') ? (_jsx("a", { href: more.href, children: more.label })) : (_jsx(Link, { href: more.href, prefetch: false, children: more.label })) })) : null] }));
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=services-grid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"services-grid.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/services-grid.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * `ServicesGrid` (S4, `services`, 3.14.0) and `ServiceCard`: route the visitor to the right service. Server components.\n *\n * Variants:\n * - `rows` (the fleet reference, AA's `ServiceRows`): one full-width row per service, the photo beside the text and\n * alternating sides on wide screens, with an optional line of scope points and the service's own link;\n * - `cards`: a grid of cards, each headed by its linked title;\n * - `compact` (suburb and region pages): a lean list of every service offered there, each a single link to its\n * service-in-suburb page or to its anchored section on the same page (`#tree-pruning`). No photos and no summaries.\n *\n * Locked rules (S4): one link per card; no prices and no before-and-afters in cards; no image on two cards; at most 8\n * `rows` or `cards` (K11's card limit, and the most in-content links that prefetch on intent), so a page that offers\n * more services lists them `compact` or links its services hub. Card links prefetch on intent (`LINK_PREFETCH_POLICY`\n * `contentCard`); compact links, the hub link and same-page anchors never prefetch.\n */\nimport Link from 'next/link'\nimport type { ReactNode } from 'react'\nimport type { BrandlabMediaEntry } from '@brandlab-digital/site-core/media'\nimport { LINK_PREFETCH_LIMITS, LINK_PREFETCH_POLICY, linkPrefetchProps, type LinkPrefetch } from '../prefetch.js'\nimport { Media, type MediaLayout } from '../primitives/media.js'\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 type { UiLink } from '../types.js'\n\nexport const SERVICES_GRID_VARIANTS = ['rows', 'cards', 'compact'] as const\nexport type ServicesGridVariant = (typeof SERVICES_GRID_VARIANTS)[number]\nexport type ServiceCardSlot = 'item' | 'media' | 'body' | 'title' | 'summary' | 'points' | 'point' | 'action' | 'link'\nexport type ServicesGridSlot = SectionSlot | SectionHeadingSlot | 'list' | 'more' | ServiceCardSlot\n/** Rendered once per service: styled from the skin stylesheet, never by class. */\nexport type ServicesGridRepeatedSlot = RepeatedSlotOf<'servicesGrid'>\nexport interface ServicesGridLayout {\n /** `cards`: columns on wide screens. Defaults to 3. */\n readonly columns?: 2 | 3 | 4\n /** `rows` and `cards`: the photo's crop. Defaults to 4:3. */\n readonly ratio?: NonNullable<MediaLayout['ratio']>\n /** `rows`: the photo's side on wide screens. Defaults to `alternate` (first photo at the start). */\n readonly mediaSide?: 'alternate' | 'start' | 'end'\n}\nexport type ServicesGridSkin = SkinEntry<ServicesGridSlot, ServicesGridVariant, ServicesGridLayout, ServicesGridRepeatedSlot>\n\n/** The most services `rows` and `cards` show (K11; the in-content intent-prefetch limit). */\nexport const SERVICES_GRID_CARD_LIMIT = LINK_PREFETCH_LIMITS.contentCard\n\n/** One service, in money order. */\nexport interface ServiceCardData {\n /** A stable slug, rendered as `data-item`. */\n readonly id: string\n /** The service's name, the card's H3 (`rows`, `cards`) or the link text (`compact`). */\n readonly title: string\n /**\n * Where the card goes: the service page, its service-in-suburb page or its anchored section on this page\n * (`#tree-pruning`). From the site's data, never built here.\n */\n readonly href: string\n /** One line on what the service is. `rows` and `cards`; never a price. */\n readonly summary?: ReactNode\n /** `rows`: a few scope points under the summary (verified service scope, never prices). */\n readonly points?: readonly string[]\n /** The service's own photo from the media manifest. `rows` and `cards`; no image on two cards. */\n readonly image?: BrandlabMediaEntry\n /**\n * `rows`: the link under the text, for example \"Tree removal in Castle Hill\". Left out, the title is the link, as on\n * `cards`. Name the service: never \"Learn more\".\n */\n readonly linkLabel?: string\n}\n\nexport interface ServiceCardProps {\n readonly service: ServiceCardData\n readonly variant: ServicesGridVariant\n /** `rows` and `cards`: the photo's `sizes`. */\n readonly imageSizes?: string\n readonly ratio?: ServicesGridLayout['ratio']\n /** `rows` and `cards` default to `intent`; `compact` is always off. */\n readonly prefetch?: LinkPrefetch\n}\n\nfunction ServiceLink({ href, prefetch, children }: { href: string; prefetch: LinkPrefetch; children: ReactNode }) {\n // A same-page section needs no route prefetch: a plain anchor.\n if (href.startsWith('#')) {\n return (\n <a href={href} {...repeatedSlot('link')}>\n {children}\n </a>\n )\n }\n return (\n <Link href={href} {...linkPrefetchProps(prefetch)} {...repeatedSlot('link')}>\n {children}\n </Link>\n )\n}\n\n/**\n * One service as a list item of `ServicesGrid` (also the building block for `RelatedServices`). Its parts are the\n * grid's repeated slots: the root sets no classes on them.\n */\nexport function ServiceCard({ service, variant, imageSizes, ratio = '4:3', prefetch }: ServiceCardProps) {\n if (variant === 'compact') {\n return (\n <li {...repeatedSlot('item')} data-item={service.id}>\n <ServiceLink href={service.href} prefetch={LINK_PREFETCH_POLICY.otherServicesInSuburb}>\n {service.title}\n </ServiceLink>\n </li>\n )\n }\n const linkPrefetch = prefetch ?? LINK_PREFETCH_POLICY.contentCard\n const separateLink = variant === 'rows' && service.linkLabel !== undefined && service.linkLabel.trim() !== ''\n return (\n <li {...repeatedSlot('item')} data-item={service.id}>\n <div {...repeatedSlot('body')}>\n <h3 {...repeatedSlot('title')}>\n {separateLink ? (\n service.title\n ) : (\n <ServiceLink href={service.href} prefetch={linkPrefetch}>\n {service.title}\n </ServiceLink>\n )}\n </h3>\n {service.summary ? <p {...repeatedSlot('summary')}>{service.summary}</p> : null}\n {variant === 'rows' && service.points && service.points.length > 0 ? (\n <ul role=\"list\" {...repeatedSlot('points')}>\n {service.points.map((point) => (\n <li key={point} {...repeatedSlot('point')}>\n {point}\n </li>\n ))}\n </ul>\n ) : null}\n {separateLink ? (\n <p {...repeatedSlot('action')}>\n <ServiceLink href={service.href} prefetch={linkPrefetch}>\n {service.linkLabel}\n </ServiceLink>\n </p>\n ) : null}\n </div>\n {service.image ? (\n <div {...repeatedSlot('media')}>\n <Media media={service.image} sizes={imageSizes ?? defaultImageSizes(variant, 3)} layout={{ ratio }} />\n </div>\n ) : null}\n </li>\n )\n}\n\n/** The photo `sizes` for each variant: half the container in rows, one column of the grid in cards. */\nexport function defaultImageSizes(variant: Exclude<ServicesGridVariant, 'compact'>, columns: 2 | 3 | 4): string {\n if (variant === 'rows') return '(min-width: 1024px) 50vw, 100vw'\n return `(min-width: 1024px) ${Math.round(100 / columns)}vw, (min-width: 640px) 50vw, 100vw`\n}\n\nexport interface ServicesGridProps extends ServicesGridSkin {\n /** The H2, for example \"Our tree services\" or \"Tree services in Castle Hill\". */\n readonly title: ReactNode\n readonly eyebrow?: ReactNode\n readonly lead?: ReactNode\n /** Every service the page offers, in money order. */\n readonly services: readonly ServiceCardData[]\n /** A link after the list, for example the services hub or \"Find your area\". Never prefetched. */\n readonly more?: UiLink\n /** `rows` and `cards`: override the photo `sizes` when the skin's grid differs from the defaults. */\n readonly imageSizes?: string\n /** Defaults to `services-heading`. */\n readonly headingId?: string\n /** Set by `SectionSequence`. */\n readonly tone?: SectionTone\n}\n\nfunction assertServices(services: readonly ServiceCardData[], variant: ServicesGridVariant): void {\n const ids = new Set<string>()\n const hrefs = new Set<string>()\n const images = new Map<string, string>()\n for (const service of services) {\n if (!service.id || ids.has(service.id)) throw new Error(`ServicesGrid: every service needs its own id (\"${service.id}\" is empty or repeated).`)\n ids.add(service.id)\n if (!service.title?.trim()) throw new Error(`ServicesGrid: the service \"${service.id}\" has no title.`)\n if (!service.href?.trim()) throw new Error(`ServicesGrid: the service \"${service.id}\" has no link.`)\n if (hrefs.has(service.href)) throw new Error(`ServicesGrid: two services link to ${service.href}; each card has its own page or section.`)\n hrefs.add(service.href)\n if (variant !== 'compact' && service.image) {\n const other = images.get(service.image.src)\n if (other) throw new Error(`ServicesGrid: \"${service.id}\" and \"${other}\" use the same image (${service.image.src}); no image is on two cards.`)\n images.set(service.image.src, service.id)\n }\n }\n if (variant !== 'compact' && services.length > SERVICES_GRID_CARD_LIMIT) {\n throw new Error(\n `ServicesGrid: ${services.length} services in ${variant}; ${variant} shows at most ${SERVICES_GRID_CARD_LIMIT} (K11). List every service with the compact variant, or show the money services and link the services hub.`,\n )\n }\n}\n\nexport function ServicesGrid({\n title,\n eyebrow,\n lead,\n services,\n more,\n imageSizes,\n headingId = 'services-heading',\n tone,\n variant = 'rows',\n layout,\n classes,\n}: ServicesGridProps) {\n if (services.length === 0) return null\n assertServices(services, variant)\n const columns = layout?.columns ?? 3\n const ratio = layout?.ratio ?? '4:3'\n const hooks =\n variant === 'compact'\n ? undefined\n : variant === 'cards'\n ? { 'data-columns': String(columns), 'data-ratio': ratio }\n : { 'data-media-side': layout?.mediaSide ?? 'alternate', 'data-ratio': ratio }\n const sizes = variant === 'compact' ? undefined : (imageSizes ?? defaultImageSizes(variant, columns))\n return (\n <Section id=\"services\" labelledBy={headingId} tone={tone} variant={variant} classes={classes} hooks={hooks}>\n <SectionHeading id={headingId} title={title} eyebrow={eyebrow} lead={lead} classes={classes} />\n <ul role=\"list\" {...slot(classes, 'list')}>\n {services.map((service) => (\n <ServiceCard key={service.id} service={service} variant={variant} imageSizes={sizes} ratio={ratio} />\n ))}\n </ul>\n {more ? (\n <p {...slot(classes, 'more')}>\n {more.href.startsWith('#') ? (\n <a href={more.href}>{more.label}</a>\n ) : (\n <Link href={more.href} prefetch={false}>\n {more.label}\n </Link>\n )}\n </p>\n ) : null}\n </Section>\n )\n}\n"]}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
import type { RepeatedSlotOf } from '../repeated-slots.js';
|
|
2
3
|
import { type SkinEntry } from '../skin.js';
|
|
3
4
|
import type { UiLink, UiPhone, UiSocial } from '../types.js';
|
|
4
5
|
export declare const SITE_FOOTER_VARIANTS: readonly ["columns", "photo"];
|
|
5
6
|
export type SiteFooterVariant = (typeof SITE_FOOTER_VARIANTS)[number];
|
|
6
7
|
export type SiteFooterSlot = 'root' | 'panel' | 'media' | 'inner' | 'brand' | 'logo' | 'description' | 'licences' | 'licenceLink' | 'social' | 'socialLink' | 'contact' | 'phone' | 'phoneLabel' | 'phoneNumber' | 'email' | 'address' | 'columns' | 'column' | 'heading' | 'toggle' | 'list' | 'link' | 'bottom' | 'bottomInner' | 'legal' | 'policies' | 'policyLink';
|
|
7
|
-
|
|
8
|
+
/** Rendered once per licence, profile, column, link or policy: styled from the skin stylesheet, never by class (3.14.0). */
|
|
9
|
+
export type SiteFooterRepeatedSlot = RepeatedSlotOf<'siteFooter'>;
|
|
10
|
+
export type SiteFooterSkin = SkinEntry<SiteFooterSlot, SiteFooterVariant, Record<never, never>, SiteFooterRepeatedSlot>;
|
|
8
11
|
/** One licence line: the text as the register shows it, and the register link when there is one. */
|
|
9
12
|
export interface SiteFooterLicence {
|
|
10
13
|
readonly text: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-footer.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/site-footer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"site-footer.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/sections/site-footer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -11,31 +11,34 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
11
11
|
*
|
|
12
12
|
* Variants: `columns`; `photo`, with the site's backdrop image for the panel in `media` (decorative, `aria-hidden`),
|
|
13
13
|
* for example Superior's sunlit water. Signature art goes in `decoration`.
|
|
14
|
+
*
|
|
15
|
+
* Prefetch is off on every internal footer link and the logo (`LINK_PREFETCH_POLICY.footer`): the footer comes into
|
|
16
|
+
* view on every full scroll, and visitors rarely follow its links.
|
|
14
17
|
*/
|
|
15
18
|
import Link from 'next/link';
|
|
16
19
|
import { Icon, SocialIcon } from '../icons.js';
|
|
17
20
|
import { PhoneLink } from '../primitives/links.js';
|
|
18
|
-
import { slot } from '../skin.js';
|
|
21
|
+
import { repeatedSlot, slot } from '../skin.js';
|
|
19
22
|
import { SiteFooterColumns } from './site-footer-columns.js';
|
|
20
23
|
export const SITE_FOOTER_VARIANTS = ['columns', 'photo'];
|
|
21
24
|
const COLUMN_KEYS = ['services', 'areas', 'company'];
|
|
22
25
|
function isExternal(href) {
|
|
23
26
|
return /^https?:\/\//i.test(href);
|
|
24
27
|
}
|
|
25
|
-
function FooterLink({ link,
|
|
26
|
-
return isExternal(link.href) ? (_jsx("a", { href: link.href, ...
|
|
28
|
+
function FooterLink({ link, slotName }) {
|
|
29
|
+
return isExternal(link.href) ? (_jsx("a", { href: link.href, ...repeatedSlot(slotName), target: "_blank", rel: "noopener noreferrer", children: link.label })) : (_jsx(Link, { href: link.href, prefetch: false, ...repeatedSlot(slotName), children: link.label }));
|
|
27
30
|
}
|
|
28
31
|
function currentYear() {
|
|
29
32
|
return Number(new Intl.DateTimeFormat('en-AU', { year: 'numeric', timeZone: 'Australia/Sydney' }).format(new Date()));
|
|
30
33
|
}
|
|
31
34
|
export function SiteFooter({ brandName, homeHref = '/', logo, description, licences = [], socials = [], phone, phoneLabel, email, address, columns, legal, media, decoration, idPrefix = 'bl-footer', variant = 'columns', classes, }) {
|
|
32
35
|
const policies = [legal.privacy, legal.terms].filter((link) => !!link);
|
|
33
|
-
return (_jsxs("footer", { "data-bl": "footer", "data-variant": variant, className: classes?.root, children: [decoration ? (_jsx("div", { "data-slot": "decoration", "aria-hidden": "true", children: decoration })) : null, _jsxs("div", { ...slot(classes, 'panel'), children: [variant === 'photo' && media ? (_jsx("div", { ...slot(classes, 'media'), "aria-hidden": "true", children: media })) : null, _jsxs("div", { ...slot(classes, 'inner'), children: [_jsxs("div", { ...slot(classes, 'brand'), children: [_jsx(Link, { href: homeHref, ...slot(classes, 'logo'), "aria-label": brandName, children: logo }), _jsx("p", { ...slot(classes, 'description'), children: description }), licences.length > 0 ? (_jsx("ul", { ...slot(classes, 'licences'), children: licences.map((licence) => (_jsx("li", { children: licence.href ? (_jsx("a", { ...
|
|
36
|
+
return (_jsxs("footer", { "data-bl": "footer", "data-variant": variant, className: classes?.root, children: [decoration ? (_jsx("div", { "data-slot": "decoration", "aria-hidden": "true", children: decoration })) : null, _jsxs("div", { ...slot(classes, 'panel'), children: [variant === 'photo' && media ? (_jsx("div", { ...slot(classes, 'media'), "aria-hidden": "true", children: media })) : null, _jsxs("div", { ...slot(classes, 'inner'), children: [_jsxs("div", { ...slot(classes, 'brand'), children: [_jsx(Link, { href: homeHref, prefetch: false, ...slot(classes, 'logo'), "aria-label": brandName, children: logo }), _jsx("p", { ...slot(classes, 'description'), children: description }), licences.length > 0 ? (_jsx("ul", { ...slot(classes, 'licences'), children: licences.map((licence) => (_jsx("li", { children: licence.href ? (_jsx("a", { ...repeatedSlot('licenceLink'), href: licence.href, target: "_blank", rel: "noopener noreferrer", children: licence.text })) : (licence.text) }, licence.text))) })) : null, socials.length > 0 ? (_jsx("ul", { ...slot(classes, 'social'), children: socials.map((social) => (_jsx("li", { children: _jsx("a", { ...repeatedSlot('socialLink'), href: social.href, "aria-label": social.label, target: "_blank", rel: "noopener noreferrer", children: _jsx(SocialIcon, { kind: social.kind }) }) }, social.href))) })) : null, _jsxs("div", { ...slot(classes, 'contact'), children: [_jsx(PhoneLink, { phone: phone, placement: "footer", label: phoneLabel, classes: { root: classes?.phone, label: classes?.phoneLabel, number: classes?.phoneNumber } }), email ? (_jsx("a", { ...slot(classes, 'email'), href: `mailto:${email}`, children: email })) : null, address ? _jsx("p", { ...slot(classes, 'address'), children: address }) : null] })] }), _jsx(SiteFooterColumns, { className: classes?.columns, children: COLUMN_KEYS.map((key) => {
|
|
34
37
|
const column = columns[key];
|
|
35
38
|
if (!column || column.links.length === 0)
|
|
36
39
|
return null;
|
|
37
40
|
const listId = `${idPrefix}-${key}`;
|
|
38
|
-
return (_jsxs("div", { ...
|
|
39
|
-
}) })] })] }), _jsx("div", { ...slot(classes, 'bottom'), children: _jsxs("div", { ...slot(classes, 'bottomInner'), children: [_jsxs("p", { ...slot(classes, 'legal'), children: ["\u00A9 ", legal.year ?? currentYear(), " ", legal.name, legal.abn ? (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", children: " \u00B7 " }), _jsxs("span", { children: ["ABN ", legal.abn] })] })) : null] }), policies.length > 0 ? (_jsx("ul", { ...slot(classes, 'policies'), children: policies.map((link) => (_jsx("li", { children: _jsx(FooterLink, { link: link,
|
|
41
|
+
return (_jsxs("div", { ...repeatedSlot('column'), "data-column": key, children: [_jsxs("h2", { ...repeatedSlot('heading'), children: [_jsx("span", { "data-slot": "headingText", children: column.heading }), _jsxs("button", { type: "button", ...repeatedSlot('toggle'), "aria-expanded": "false", "aria-controls": listId, hidden: true, children: [_jsx("span", { children: column.heading }), _jsx(Icon, { name: "chevron" })] })] }), _jsx("ul", { id: listId, ...repeatedSlot('list'), children: column.links.map((link) => (_jsx("li", { children: _jsx(FooterLink, { link: link, slotName: "link" }) }, link.href))) })] }, key));
|
|
42
|
+
}) })] })] }), _jsx("div", { ...slot(classes, 'bottom'), children: _jsxs("div", { ...slot(classes, 'bottomInner'), children: [_jsxs("p", { ...slot(classes, 'legal'), children: ["\u00A9 ", legal.year ?? currentYear(), " ", legal.name, legal.abn ? (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", children: " \u00B7 " }), _jsxs("span", { children: ["ABN ", legal.abn] })] })) : null] }), policies.length > 0 ? (_jsx("ul", { ...slot(classes, 'policies'), children: policies.map((link) => (_jsx("li", { children: _jsx(FooterLink, { link: link, slotName: "policyLink" }) }, link.href))) })) : null] }) })] }));
|
|
40
43
|
}
|
|
41
44
|
//# sourceMappingURL=site-footer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-footer.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/site-footer.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * S27 `SiteFooter`: the v1 §6 footer, structured the same on every site. Server component; one small client island\n * (`SiteFooterColumns`) turns the columns into a phone accordion.\n *\n * Column 1: the logo, a short description, one line per licence (with its register link when given), social and\n * Business Profile icons, then the phone (`phone_link_click` placement `footer`), the email and the address only when\n * it is public. Then Services, Areas and Company, each under a real H2, in that order. A bottom bar: © year name ·\n * ABN · the privacy and terms links. Never a quote button, \"based in\", opening hours, a guides column or a \"back to\n * top\" text link: the props have no place for them.\n *\n * Variants: `columns`; `photo`, with the site's backdrop image for the panel in `media` (decorative, `aria-hidden`),\n * for example Superior's sunlit water. Signature art goes in `decoration`.\n */\nimport Link from 'next/link'\nimport type { ReactNode } from 'react'\nimport { Icon, SocialIcon } from '../icons.js'\nimport { PhoneLink } from '../primitives/links.js'\nimport { slot, type SkinEntry } from '../skin.js'\nimport type { UiLink, UiPhone, UiSocial } from '../types.js'\nimport { SiteFooterColumns } from './site-footer-columns.js'\n\nexport const SITE_FOOTER_VARIANTS = ['columns', 'photo'] as const\nexport type SiteFooterVariant = (typeof SITE_FOOTER_VARIANTS)[number]\n\nexport type SiteFooterSlot =\n | 'root'\n | 'panel'\n | 'media'\n | 'inner'\n | 'brand'\n | 'logo'\n | 'description'\n | 'licences'\n | 'licenceLink'\n | 'social'\n | 'socialLink'\n | 'contact'\n | 'phone'\n | 'phoneLabel'\n | 'phoneNumber'\n | 'email'\n | 'address'\n | 'columns'\n | 'column'\n | 'heading'\n | 'toggle'\n | 'list'\n | 'link'\n | 'bottom'\n | 'bottomInner'\n | 'legal'\n | 'policies'\n | 'policyLink'\n\nexport type SiteFooterSkin = SkinEntry<SiteFooterSlot, SiteFooterVariant>\n\n/** One licence line: the text as the register shows it, and the register link when there is one. */\nexport interface SiteFooterLicence {\n readonly text: string\n readonly href?: string\n}\n\nexport interface SiteFooterColumn {\n /** The H2 text, for example \"Services\". */\n readonly heading: string\n readonly links: readonly UiLink[]\n}\n\nexport interface SiteFooterProps extends SiteFooterSkin {\n /** The logo link's accessible name. */\n readonly brandName: string\n readonly homeHref?: string\n /** The site's logo node (decorative inside the named link). */\n readonly logo: ReactNode\n /** Two or three lines about the business. */\n readonly description: string\n readonly licences?: readonly SiteFooterLicence[]\n readonly socials?: readonly UiSocial[]\n readonly phone: UiPhone\n /** Optional words before the number, for example \"Call us\". */\n readonly phoneLabel?: string\n readonly email?: string\n /** Only when the business shows its address publicly. */\n readonly address?: string\n /** Services, Areas and Company, rendered in that order; leave one out when the site has nothing true for it. */\n readonly columns: {\n readonly services?: SiteFooterColumn\n readonly areas?: SiteFooterColumn\n readonly company?: SiteFooterColumn\n }\n readonly legal: {\n /** The legal or trading name. */\n readonly name: string\n readonly abn?: string\n /** Defaults to the current year in Sydney. */\n readonly year?: number\n /** The privacy policy link, labelled by the site. */\n readonly privacy?: UiLink\n /** The terms link, labelled by the site. */\n readonly terms?: UiLink\n }\n /** The `photo` variant's backdrop image for the panel (decorative). */\n readonly media?: ReactNode\n /** Signature art, rendered `aria-hidden`. */\n readonly decoration?: ReactNode\n /** Prefix for the column list ids (default `bl-footer`). */\n readonly idPrefix?: string\n}\n\nconst COLUMN_KEYS = ['services', 'areas', 'company'] as const\n\nfunction isExternal(href: string) {\n return /^https?:\\/\\//i.test(href)\n}\n\nfunction FooterLink({ link, hooks }: { link: UiLink; hooks?: { 'data-slot': string; className: string | undefined } }) {\n return isExternal(link.href) ? (\n <a href={link.href} {...hooks} target=\"_blank\" rel=\"noopener noreferrer\">\n {link.label}\n </a>\n ) : (\n <Link href={link.href} {...hooks}>\n {link.label}\n </Link>\n )\n}\n\nfunction currentYear() {\n return Number(new Intl.DateTimeFormat('en-AU', { year: 'numeric', timeZone: 'Australia/Sydney' }).format(new Date()))\n}\n\nexport function SiteFooter({\n brandName,\n homeHref = '/',\n logo,\n description,\n licences = [],\n socials = [],\n phone,\n phoneLabel,\n email,\n address,\n columns,\n legal,\n media,\n decoration,\n idPrefix = 'bl-footer',\n variant = 'columns',\n classes,\n}: SiteFooterProps) {\n const policies = [legal.privacy, legal.terms].filter((link): link is UiLink => !!link)\n return (\n <footer data-bl=\"footer\" data-variant={variant} className={classes?.root}>\n {decoration ? (\n <div data-slot=\"decoration\" aria-hidden=\"true\">\n {decoration}\n </div>\n ) : null}\n <div {...slot(classes, 'panel')}>\n {variant === 'photo' && media ? (\n <div {...slot(classes, 'media')} aria-hidden=\"true\">\n {media}\n </div>\n ) : null}\n <div {...slot(classes, 'inner')}>\n <div {...slot(classes, 'brand')}>\n <Link href={homeHref} {...slot(classes, 'logo')} aria-label={brandName}>\n {logo}\n </Link>\n <p {...slot(classes, 'description')}>{description}</p>\n {licences.length > 0 ? (\n <ul {...slot(classes, 'licences')}>\n {licences.map((licence) => (\n <li key={licence.text}>\n {licence.href ? (\n <a {...slot(classes, 'licenceLink')} href={licence.href} target=\"_blank\" rel=\"noopener noreferrer\">\n {licence.text}\n </a>\n ) : (\n licence.text\n )}\n </li>\n ))}\n </ul>\n ) : null}\n {socials.length > 0 ? (\n <ul {...slot(classes, 'social')}>\n {socials.map((social) => (\n <li key={social.href}>\n <a {...slot(classes, 'socialLink')} href={social.href} aria-label={social.label} target=\"_blank\" rel=\"noopener noreferrer\">\n <SocialIcon kind={social.kind} />\n </a>\n </li>\n ))}\n </ul>\n ) : null}\n <div {...slot(classes, 'contact')}>\n <PhoneLink phone={phone} placement=\"footer\" label={phoneLabel} classes={{ root: classes?.phone, label: classes?.phoneLabel, number: classes?.phoneNumber }} />\n {email ? (\n <a {...slot(classes, 'email')} href={`mailto:${email}`}>\n {email}\n </a>\n ) : null}\n {address ? <p {...slot(classes, 'address')}>{address}</p> : null}\n </div>\n </div>\n <SiteFooterColumns className={classes?.columns}>\n {COLUMN_KEYS.map((key) => {\n const column = columns[key]\n if (!column || column.links.length === 0) return null\n const listId = `${idPrefix}-${key}`\n return (\n <div key={key} {...slot(classes, 'column')} data-column={key}>\n <h2 {...slot(classes, 'heading')}>\n <span data-slot=\"headingText\">{column.heading}</span>\n <button type=\"button\" {...slot(classes, 'toggle')} aria-expanded=\"false\" aria-controls={listId} hidden>\n <span>{column.heading}</span>\n <Icon name=\"chevron\" />\n </button>\n </h2>\n <ul id={listId} {...slot(classes, 'list')}>\n {column.links.map((link) => (\n <li key={link.href}>\n <FooterLink link={link} hooks={slot(classes, 'link')} />\n </li>\n ))}\n </ul>\n </div>\n )\n })}\n </SiteFooterColumns>\n </div>\n </div>\n <div {...slot(classes, 'bottom')}>\n <div {...slot(classes, 'bottomInner')}>\n <p {...slot(classes, 'legal')}>\n © {legal.year ?? currentYear()} {legal.name}\n {legal.abn ? (\n <>\n <span aria-hidden=\"true\"> · </span>\n <span>ABN {legal.abn}</span>\n </>\n ) : null}\n </p>\n {policies.length > 0 ? (\n <ul {...slot(classes, 'policies')}>\n {policies.map((link) => (\n <li key={link.href}>\n <FooterLink link={link} hooks={slot(classes, 'policyLink')} />\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n </div>\n </footer>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"site-footer.js","sourceRoot":"","sources":["../../../src/ui-kit/sections/site-footer.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * S27 `SiteFooter`: the v1 §6 footer, structured the same on every site. Server component; one small client island\n * (`SiteFooterColumns`) turns the columns into a phone accordion.\n *\n * Column 1: the logo, a short description, one line per licence (with its register link when given), social and\n * Business Profile icons, then the phone (`phone_link_click` placement `footer`), the email and the address only when\n * it is public. Then Services, Areas and Company, each under a real H2, in that order. A bottom bar: © year name ·\n * ABN · the privacy and terms links. Never a quote button, \"based in\", opening hours, a guides column or a \"back to\n * top\" text link: the props have no place for them.\n *\n * Variants: `columns`; `photo`, with the site's backdrop image for the panel in `media` (decorative, `aria-hidden`),\n * for example Superior's sunlit water. Signature art goes in `decoration`.\n *\n * Prefetch is off on every internal footer link and the logo (`LINK_PREFETCH_POLICY.footer`): the footer comes into\n * view on every full scroll, and visitors rarely follow its links.\n */\nimport Link from 'next/link'\nimport type { ReactNode } from 'react'\nimport { Icon, SocialIcon } from '../icons.js'\nimport { PhoneLink } from '../primitives/links.js'\nimport type { RepeatedSlotOf } from '../repeated-slots.js'\nimport { repeatedSlot, slot, type SkinEntry } from '../skin.js'\nimport type { UiLink, UiPhone, UiSocial } from '../types.js'\nimport { SiteFooterColumns } from './site-footer-columns.js'\n\nexport const SITE_FOOTER_VARIANTS = ['columns', 'photo'] as const\nexport type SiteFooterVariant = (typeof SITE_FOOTER_VARIANTS)[number]\n\nexport type SiteFooterSlot =\n | 'root'\n | 'panel'\n | 'media'\n | 'inner'\n | 'brand'\n | 'logo'\n | 'description'\n | 'licences'\n | 'licenceLink'\n | 'social'\n | 'socialLink'\n | 'contact'\n | 'phone'\n | 'phoneLabel'\n | 'phoneNumber'\n | 'email'\n | 'address'\n | 'columns'\n | 'column'\n | 'heading'\n | 'toggle'\n | 'list'\n | 'link'\n | 'bottom'\n | 'bottomInner'\n | 'legal'\n | 'policies'\n | 'policyLink'\n\n/** Rendered once per licence, profile, column, link or policy: styled from the skin stylesheet, never by class (3.14.0). */\nexport type SiteFooterRepeatedSlot = RepeatedSlotOf<'siteFooter'>\nexport type SiteFooterSkin = SkinEntry<SiteFooterSlot, SiteFooterVariant, Record<never, never>, SiteFooterRepeatedSlot>\n\n/** One licence line: the text as the register shows it, and the register link when there is one. */\nexport interface SiteFooterLicence {\n readonly text: string\n readonly href?: string\n}\n\nexport interface SiteFooterColumn {\n /** The H2 text, for example \"Services\". */\n readonly heading: string\n readonly links: readonly UiLink[]\n}\n\nexport interface SiteFooterProps extends SiteFooterSkin {\n /** The logo link's accessible name. */\n readonly brandName: string\n readonly homeHref?: string\n /** The site's logo node (decorative inside the named link). */\n readonly logo: ReactNode\n /** Two or three lines about the business. */\n readonly description: string\n readonly licences?: readonly SiteFooterLicence[]\n readonly socials?: readonly UiSocial[]\n readonly phone: UiPhone\n /** Optional words before the number, for example \"Call us\". */\n readonly phoneLabel?: string\n readonly email?: string\n /** Only when the business shows its address publicly. */\n readonly address?: string\n /** Services, Areas and Company, rendered in that order; leave one out when the site has nothing true for it. */\n readonly columns: {\n readonly services?: SiteFooterColumn\n readonly areas?: SiteFooterColumn\n readonly company?: SiteFooterColumn\n }\n readonly legal: {\n /** The legal or trading name. */\n readonly name: string\n readonly abn?: string\n /** Defaults to the current year in Sydney. */\n readonly year?: number\n /** The privacy policy link, labelled by the site. */\n readonly privacy?: UiLink\n /** The terms link, labelled by the site. */\n readonly terms?: UiLink\n }\n /** The `photo` variant's backdrop image for the panel (decorative). */\n readonly media?: ReactNode\n /** Signature art, rendered `aria-hidden`. */\n readonly decoration?: ReactNode\n /** Prefix for the column list ids (default `bl-footer`). */\n readonly idPrefix?: string\n}\n\nconst COLUMN_KEYS = ['services', 'areas', 'company'] as const\n\nfunction isExternal(href: string) {\n return /^https?:\\/\\//i.test(href)\n}\n\nfunction FooterLink({ link, slotName }: { link: UiLink; slotName: 'link' | 'policyLink' }) {\n return isExternal(link.href) ? (\n <a href={link.href} {...repeatedSlot(slotName)} target=\"_blank\" rel=\"noopener noreferrer\">\n {link.label}\n </a>\n ) : (\n <Link href={link.href} prefetch={false} {...repeatedSlot(slotName)}>\n {link.label}\n </Link>\n )\n}\n\nfunction currentYear() {\n return Number(new Intl.DateTimeFormat('en-AU', { year: 'numeric', timeZone: 'Australia/Sydney' }).format(new Date()))\n}\n\nexport function SiteFooter({\n brandName,\n homeHref = '/',\n logo,\n description,\n licences = [],\n socials = [],\n phone,\n phoneLabel,\n email,\n address,\n columns,\n legal,\n media,\n decoration,\n idPrefix = 'bl-footer',\n variant = 'columns',\n classes,\n}: SiteFooterProps) {\n const policies = [legal.privacy, legal.terms].filter((link): link is UiLink => !!link)\n return (\n <footer data-bl=\"footer\" data-variant={variant} className={classes?.root}>\n {decoration ? (\n <div data-slot=\"decoration\" aria-hidden=\"true\">\n {decoration}\n </div>\n ) : null}\n <div {...slot(classes, 'panel')}>\n {variant === 'photo' && media ? (\n <div {...slot(classes, 'media')} aria-hidden=\"true\">\n {media}\n </div>\n ) : null}\n <div {...slot(classes, 'inner')}>\n <div {...slot(classes, 'brand')}>\n <Link href={homeHref} prefetch={false} {...slot(classes, 'logo')} aria-label={brandName}>\n {logo}\n </Link>\n <p {...slot(classes, 'description')}>{description}</p>\n {licences.length > 0 ? (\n <ul {...slot(classes, 'licences')}>\n {licences.map((licence) => (\n <li key={licence.text}>\n {licence.href ? (\n <a {...repeatedSlot('licenceLink')} href={licence.href} target=\"_blank\" rel=\"noopener noreferrer\">\n {licence.text}\n </a>\n ) : (\n licence.text\n )}\n </li>\n ))}\n </ul>\n ) : null}\n {socials.length > 0 ? (\n <ul {...slot(classes, 'social')}>\n {socials.map((social) => (\n <li key={social.href}>\n <a {...repeatedSlot('socialLink')} href={social.href} aria-label={social.label} target=\"_blank\" rel=\"noopener noreferrer\">\n <SocialIcon kind={social.kind} />\n </a>\n </li>\n ))}\n </ul>\n ) : null}\n <div {...slot(classes, 'contact')}>\n <PhoneLink phone={phone} placement=\"footer\" label={phoneLabel} classes={{ root: classes?.phone, label: classes?.phoneLabel, number: classes?.phoneNumber }} />\n {email ? (\n <a {...slot(classes, 'email')} href={`mailto:${email}`}>\n {email}\n </a>\n ) : null}\n {address ? <p {...slot(classes, 'address')}>{address}</p> : null}\n </div>\n </div>\n <SiteFooterColumns className={classes?.columns}>\n {COLUMN_KEYS.map((key) => {\n const column = columns[key]\n if (!column || column.links.length === 0) return null\n const listId = `${idPrefix}-${key}`\n return (\n <div key={key} {...repeatedSlot('column')} data-column={key}>\n <h2 {...repeatedSlot('heading')}>\n <span data-slot=\"headingText\">{column.heading}</span>\n <button type=\"button\" {...repeatedSlot('toggle')} aria-expanded=\"false\" aria-controls={listId} hidden>\n <span>{column.heading}</span>\n <Icon name=\"chevron\" />\n </button>\n </h2>\n <ul id={listId} {...repeatedSlot('list')}>\n {column.links.map((link) => (\n <li key={link.href}>\n <FooterLink link={link} slotName=\"link\" />\n </li>\n ))}\n </ul>\n </div>\n )\n })}\n </SiteFooterColumns>\n </div>\n </div>\n <div {...slot(classes, 'bottom')}>\n <div {...slot(classes, 'bottomInner')}>\n <p {...slot(classes, 'legal')}>\n © {legal.year ?? currentYear()} {legal.name}\n {legal.abn ? (\n <>\n <span aria-hidden=\"true\"> · </span>\n <span>ABN {legal.abn}</span>\n </>\n ) : null}\n </p>\n {policies.length > 0 ? (\n <ul {...slot(classes, 'policies')}>\n {policies.map((link) => (\n <li key={link.href}>\n <FooterLink link={link} slotName=\"policyLink\" />\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n </div>\n </footer>\n )\n}\n"]}
|
|
@@ -7,9 +7,23 @@ export declare function CurrentLink({ href, slot, hub, prefetch, className, chil
|
|
|
7
7
|
readonly href: string;
|
|
8
8
|
readonly slot: string;
|
|
9
9
|
readonly hub?: boolean;
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* False for links in panels and the drawer: hidden panels count as in view and would prefetch every menu page. The
|
|
12
|
+
* current page is never prefetched, whatever this says.
|
|
13
|
+
*/
|
|
11
14
|
readonly prefetch?: boolean | undefined;
|
|
12
15
|
readonly className?: string | undefined;
|
|
13
16
|
readonly children: ReactNode;
|
|
14
17
|
}): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
/**
|
|
19
|
+
* The header logo's home link: prefetch stays Next's default except on the home page itself, which is never
|
|
20
|
+
* prefetched. No `aria-current`: the logo is named by the brand, not as a menu item.
|
|
21
|
+
*/
|
|
22
|
+
export declare function LogoLink({ href, label, slot, className, children, }: {
|
|
23
|
+
readonly href: string;
|
|
24
|
+
readonly label: string;
|
|
25
|
+
readonly slot: string;
|
|
26
|
+
readonly className?: string | undefined;
|
|
27
|
+
readonly children: ReactNode;
|
|
28
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
15
29
|
//# sourceMappingURL=current-link.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"current-link.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/shell/current-link.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAOtC,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,WAG3D;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,WAO1E;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,IAAI,EACJ,GAAW,EACX,QAAQ,EACR,SAAS,EACT,QAAQ,GACT,EAAE;IACD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;IACtB
|
|
1
|
+
{"version":3,"file":"current-link.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/shell/current-link.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAOtC,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,WAG3D;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,WAO1E;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,IAAI,EACJ,GAAW,EACX,QAAQ,EACR,SAAS,EACT,QAAQ,GACT,EAAE;IACD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;IACtB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC7B,2CAeA;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,SAAS,EACT,QAAQ,GACT,EAAE;IACD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC7B,2CAOA"}
|
|
@@ -29,6 +29,15 @@ export function isCurrentSection(pathname, paths) {
|
|
|
29
29
|
}
|
|
30
30
|
export function CurrentLink({ href, slot, hub = false, prefetch, className, children, }) {
|
|
31
31
|
const pathname = usePathname();
|
|
32
|
-
|
|
32
|
+
const current = isCurrentPage(pathname, href);
|
|
33
|
+
return (_jsx(Link, { href: href, prefetch: current ? false : prefetch, "data-slot": slot, "data-hub": hub ? 'true' : undefined, "aria-current": current ? 'page' : undefined, className: className, children: children }));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The header logo's home link: prefetch stays Next's default except on the home page itself, which is never
|
|
37
|
+
* prefetched. No `aria-current`: the logo is named by the brand, not as a menu item.
|
|
38
|
+
*/
|
|
39
|
+
export function LogoLink({ href, label, slot, className, children, }) {
|
|
40
|
+
const pathname = usePathname();
|
|
41
|
+
return (_jsx(Link, { href: href, prefetch: isCurrentPage(pathname, href) ? false : undefined, "aria-label": label, "data-slot": slot, className: className, children: children }));
|
|
33
42
|
}
|
|
34
43
|
//# sourceMappingURL=current-link.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"current-link.js","sourceRoot":"","sources":["../../../src/ui-kit/shell/current-link.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;GAIG;AACH,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG7C,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAA;AACxC,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,IAAY;IAC1D,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAChE,OAAO,aAAa,CAAC,QAAQ,CAAC,KAAK,aAAa,CAAC,IAAI,CAAC,CAAA;AACxD,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,KAAwB;IACzE,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAA;IACvC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QACzB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;QAChE,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAA;QACnC,OAAO,OAAO,KAAK,OAAO,IAAI,CAAC,OAAO,KAAK,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,CAAA;IACtF,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAC1B,IAAI,EACJ,IAAI,EACJ,GAAG,GAAG,KAAK,EACX,QAAQ,EACR,SAAS,EACT,QAAQ,
|
|
1
|
+
{"version":3,"file":"current-link.js","sourceRoot":"","sources":["../../../src/ui-kit/shell/current-link.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;GAIG;AACH,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG7C,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAA;AACxC,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,IAAY;IAC1D,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAChE,OAAO,aAAa,CAAC,QAAQ,CAAC,KAAK,aAAa,CAAC,IAAI,CAAC,CAAA;AACxD,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,KAAwB;IACzE,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAA;IACvC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QACzB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;QAChE,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAA;QACnC,OAAO,OAAO,KAAK,OAAO,IAAI,CAAC,OAAO,KAAK,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,CAAA;IACtF,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAC1B,IAAI,EACJ,IAAI,EACJ,GAAG,GAAG,KAAK,EACX,QAAQ,EACR,SAAS,EACT,QAAQ,GAYT;IACC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;IAC9B,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;IAC7C,OAAO,CACL,KAAC,IAAI,IACH,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,eACzB,IAAI,cACL,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,kBACpB,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAC1C,SAAS,EAAE,SAAS,YAEnB,QAAQ,GACJ,CACR,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,EACvB,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,SAAS,EACT,QAAQ,GAOT;IACC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;IAC9B,OAAO,CACL,KAAC,IAAI,IAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,gBAAc,KAAK,eAAa,IAAI,EAAE,SAAS,EAAE,SAAS,YACpI,QAAQ,GACJ,CACR,CAAA;AACH,CAAC","sourcesContent":["'use client'\n\n/**\n * A menu link that marks itself `aria-current=\"page\"` on its own page. A client leaf only because the root layout\n * can't read the route: it receives one href and its server-rendered label, and renders in the server HTML with the\n * right state.\n */\nimport Link from 'next/link'\nimport { usePathname } from 'next/navigation'\nimport type { ReactNode } from 'react'\n\nfunction normalisePath(path: string) {\n const bare = path.split(/[?#]/, 1)[0] ?? ''\n return bare.replace(/\\/+$/, '') || '/'\n}\n\n/** True when `href` is the current page. External links are never current. */\nexport function isCurrentPage(pathname: string, href: string) {\n if (!href.startsWith('/') || href.startsWith('//')) return false\n return normalisePath(pathname) === normalisePath(href)\n}\n\n/** True when the page is one of `paths` or below it (the home path matches only itself). */\nexport function isCurrentSection(pathname: string, paths: readonly string[]) {\n const current = normalisePath(pathname)\n return paths.some((path) => {\n if (!path.startsWith('/') || path.startsWith('//')) return false\n const section = normalisePath(path)\n return current === section || (section !== '/' && current.startsWith(`${section}/`))\n })\n}\n\nexport function CurrentLink({\n href,\n slot,\n hub = false,\n prefetch,\n className,\n children,\n}: {\n readonly href: string\n readonly slot: string\n readonly hub?: boolean\n /**\n * False for links in panels and the drawer: hidden panels count as in view and would prefetch every menu page. The\n * current page is never prefetched, whatever this says.\n */\n readonly prefetch?: boolean | undefined\n readonly className?: string | undefined\n readonly children: ReactNode\n}) {\n const pathname = usePathname()\n const current = isCurrentPage(pathname, href)\n return (\n <Link\n href={href}\n prefetch={current ? false : prefetch}\n data-slot={slot}\n data-hub={hub ? 'true' : undefined}\n aria-current={current ? 'page' : undefined}\n className={className}\n >\n {children}\n </Link>\n )\n}\n\n/**\n * The header logo's home link: prefetch stays Next's default except on the home page itself, which is never\n * prefetched. No `aria-current`: the logo is named by the brand, not as a menu item.\n */\nexport function LogoLink({\n href,\n label,\n slot,\n className,\n children,\n}: {\n readonly href: string\n readonly label: string\n readonly slot: string\n readonly className?: string | undefined\n readonly children: ReactNode\n}) {\n const pathname = usePathname()\n return (\n <Link href={href} prefetch={isCurrentPage(pathname, href) ? false : undefined} aria-label={label} data-slot={slot} className={className}>\n {children}\n </Link>\n )\n}\n"]}
|
|
@@ -3,4 +3,5 @@ export { MOBILE_DRAWER_VARIANTS, MobileDrawer, type MobileDrawerLayout, type Mob
|
|
|
3
3
|
export { UtilityBar, type UtilityBarLayout, type UtilityBarProps, type UtilityBarSkin, type UtilityBarSlot, type UtilityLicence, type UtilityRating, } from './utility-bar.js';
|
|
4
4
|
export { MOBILE_CALL_BAR_VARIANTS, MobileCallBar, type MobileCallBarProps, type MobileCallBarSkin, type MobileCallBarSlot, type MobileCallBarVariant, } from './mobile-call-bar.js';
|
|
5
5
|
export { BACK_TO_TOP_OFFSETS, BackToTop, type BackToTopLayout, type BackToTopProps, type BackToTopSkin, type BackToTopSlot } from './back-to-top.js';
|
|
6
|
+
export { IntentPrefetch, intentPrefetchTarget } from './intent-prefetch.js';
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/shell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,UAAU,GAChB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,EACV,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,wBAAwB,EACxB,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/shell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,UAAU,GAChB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,EACV,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,wBAAwB,EACxB,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACpJ,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA"}
|
|
@@ -3,4 +3,5 @@ export { MOBILE_DRAWER_VARIANTS, MobileDrawer, } from './mobile-drawer.js';
|
|
|
3
3
|
export { UtilityBar, } from './utility-bar.js';
|
|
4
4
|
export { MOBILE_CALL_BAR_VARIANTS, MobileCallBar, } from './mobile-call-bar.js';
|
|
5
5
|
export { BACK_TO_TOP_OFFSETS, BackToTop } from './back-to-top.js';
|
|
6
|
+
export { IntentPrefetch, intentPrefetchTarget } from './intent-prefetch.js';
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui-kit/shell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,UAAU,GAMX,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,sBAAsB,EACtB,YAAY,GAOb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,GAOX,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,wBAAwB,EACxB,aAAa,GAKd,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAqF,MAAM,kBAAkB,CAAA","sourcesContent":["export {\n SITE_HEADER_VARIANTS,\n SiteHeader,\n type SiteHeaderLayout,\n type SiteHeaderProps,\n type SiteHeaderSkin,\n type SiteHeaderSlot,\n type SiteHeaderVariant,\n} from './site-header.js'\nexport {\n MOBILE_DRAWER_VARIANTS,\n MobileDrawer,\n type MobileDrawerLayout,\n type MobileDrawerProps,\n type MobileDrawerSkin,\n type MobileDrawerSlot,\n type MobileDrawerVariant,\n type ShellQuote,\n} from './mobile-drawer.js'\nexport {\n UtilityBar,\n type UtilityBarLayout,\n type UtilityBarProps,\n type UtilityBarSkin,\n type UtilityBarSlot,\n type UtilityLicence,\n type UtilityRating,\n} from './utility-bar.js'\nexport {\n MOBILE_CALL_BAR_VARIANTS,\n MobileCallBar,\n type MobileCallBarProps,\n type MobileCallBarSkin,\n type MobileCallBarSlot,\n type MobileCallBarVariant,\n} from './mobile-call-bar.js'\nexport { BACK_TO_TOP_OFFSETS, BackToTop, type BackToTopLayout, type BackToTopProps, type BackToTopSkin, type BackToTopSlot } from './back-to-top.js'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui-kit/shell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,UAAU,GAMX,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,sBAAsB,EACtB,YAAY,GAOb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,GAOX,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,wBAAwB,EACxB,aAAa,GAKd,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAqF,MAAM,kBAAkB,CAAA;AACpJ,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA","sourcesContent":["export {\n SITE_HEADER_VARIANTS,\n SiteHeader,\n type SiteHeaderLayout,\n type SiteHeaderProps,\n type SiteHeaderSkin,\n type SiteHeaderSlot,\n type SiteHeaderVariant,\n} from './site-header.js'\nexport {\n MOBILE_DRAWER_VARIANTS,\n MobileDrawer,\n type MobileDrawerLayout,\n type MobileDrawerProps,\n type MobileDrawerSkin,\n type MobileDrawerSlot,\n type MobileDrawerVariant,\n type ShellQuote,\n} from './mobile-drawer.js'\nexport {\n UtilityBar,\n type UtilityBarLayout,\n type UtilityBarProps,\n type UtilityBarSkin,\n type UtilityBarSlot,\n type UtilityLicence,\n type UtilityRating,\n} from './utility-bar.js'\nexport {\n MOBILE_CALL_BAR_VARIANTS,\n MobileCallBar,\n type MobileCallBarProps,\n type MobileCallBarSkin,\n type MobileCallBarSlot,\n type MobileCallBarVariant,\n} from './mobile-call-bar.js'\nexport { BACK_TO_TOP_OFFSETS, BackToTop, type BackToTopLayout, type BackToTopProps, type BackToTopSkin, type BackToTopSlot } from './back-to-top.js'\nexport { IntentPrefetch, intentPrefetchTarget } from './intent-prefetch.js'\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** The site path an intent link points to, without its fragment; null for another site or no href. */
|
|
2
|
+
export declare function intentPrefetchTarget(href: string | null): string | null;
|
|
3
|
+
export declare function IntentPrefetch(): null;
|
|
4
|
+
//# sourceMappingURL=intent-prefetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent-prefetch.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/shell/intent-prefetch.tsx"],"names":[],"mappings":"AA4BA,sGAAsG;AACtG,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAIvE;AAOD,wBAAgB,cAAc,IAAI,IAAI,CAsBrC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
/**
|
|
3
|
+
* `IntentPrefetch`: prefetches an in-content card link when a visitor shows intent, not when it scrolls into view.
|
|
4
|
+
* Links with `prefetch="intent"` (ButtonLink, TextLink, QuoteButton) render `prefetch={false}` and
|
|
5
|
+
* `data-prefetch="intent"`; this island holds one document listener for pointer entry, focus and touch, and calls
|
|
6
|
+
* `router.prefetch(href)` for the one link the visitor points at, focuses or touches, once per page address. A touch
|
|
7
|
+
* prefetches only the touched link, never the list around it; the current page and links to other sites are never
|
|
8
|
+
* prefetched.
|
|
9
|
+
*
|
|
10
|
+
* Mount it once, in the root layout, beside the header:
|
|
11
|
+
*
|
|
12
|
+
* // src/app/layout.tsx
|
|
13
|
+
* import { IntentPrefetch } from '@brandlab-digital/site-next/ui-kit/shell'
|
|
14
|
+
* <body>
|
|
15
|
+
* <SiteHeader … />
|
|
16
|
+
* <IntentPrefetch />
|
|
17
|
+
* …
|
|
18
|
+
* </body>
|
|
19
|
+
*
|
|
20
|
+
* It renders nothing; without scripts the links still work and simply do not prefetch.
|
|
21
|
+
*/
|
|
22
|
+
import { useRouter } from 'next/navigation';
|
|
23
|
+
import { useEffect } from 'react';
|
|
24
|
+
import { INTENT_PREFETCH_SELECTOR } from '../prefetch.js';
|
|
25
|
+
const INTENT_EVENTS = ['pointerenter', 'focusin', 'touchstart'];
|
|
26
|
+
/** The site path an intent link points to, without its fragment; null for another site or no href. */
|
|
27
|
+
export function intentPrefetchTarget(href) {
|
|
28
|
+
if (!href || !href.startsWith('/') || href.startsWith('//'))
|
|
29
|
+
return null;
|
|
30
|
+
const target = href.split('#', 1)[0] ?? '';
|
|
31
|
+
return target || null;
|
|
32
|
+
}
|
|
33
|
+
function samePage(target, location) {
|
|
34
|
+
const path = (value) => value.replace(/\/+$/, '') || '/';
|
|
35
|
+
return path(target.split('?', 1)[0] ?? '') === path(location.pathname);
|
|
36
|
+
}
|
|
37
|
+
export function IntentPrefetch() {
|
|
38
|
+
const router = useRouter();
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const prefetched = new Set();
|
|
41
|
+
const onIntent = (event) => {
|
|
42
|
+
const element = event.target instanceof Element ? event.target : null;
|
|
43
|
+
const link = element?.closest(INTENT_PREFETCH_SELECTOR);
|
|
44
|
+
const target = intentPrefetchTarget(link?.getAttribute('href') ?? null);
|
|
45
|
+
if (!target || prefetched.has(target) || samePage(target, window.location))
|
|
46
|
+
return;
|
|
47
|
+
prefetched.add(target);
|
|
48
|
+
router.prefetch(target);
|
|
49
|
+
};
|
|
50
|
+
// Capture, so pointerenter (which does not bubble) reaches the document; passive, so touch never waits on it.
|
|
51
|
+
const options = { capture: true, passive: true };
|
|
52
|
+
for (const type of INTENT_EVENTS)
|
|
53
|
+
document.addEventListener(type, onIntent, options);
|
|
54
|
+
return () => {
|
|
55
|
+
for (const type of INTENT_EVENTS)
|
|
56
|
+
document.removeEventListener(type, onIntent, options);
|
|
57
|
+
};
|
|
58
|
+
}, [router]);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=intent-prefetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent-prefetch.js","sourceRoot":"","sources":["../../../src/ui-kit/shell/intent-prefetch.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAEzD,MAAM,aAAa,GAAG,CAAC,cAAc,EAAE,SAAS,EAAE,YAAY,CAAU,CAAA;AAExE,sGAAsG;AACtG,MAAM,UAAU,oBAAoB,CAAC,IAAmB;IACtD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IACxE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC1C,OAAO,MAAM,IAAI,IAAI,CAAA;AACvB,CAAC;AAED,SAAS,QAAQ,CAAC,MAAc,EAAE,QAAkB;IAClD,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAA;IAChE,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AACxE,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAE1B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAA;QACpC,MAAM,QAAQ,GAAG,CAAC,KAAY,EAAE,EAAE;YAChC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;YACrE,MAAM,IAAI,GAAG,OAAO,EAAE,OAAO,CAAoB,wBAAwB,CAAC,CAAA;YAC1E,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAA;YACvE,IAAI,CAAC,MAAM,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC;gBAAE,OAAM;YAClF,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;YACtB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;QACzB,CAAC,CAAA;QACD,8GAA8G;QAC9G,MAAM,OAAO,GAA4B,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QACzE,KAAK,MAAM,IAAI,IAAI,aAAa;YAAE,QAAQ,CAAC,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QACpF,OAAO,GAAG,EAAE;YACV,KAAK,MAAM,IAAI,IAAI,aAAa;gBAAE,QAAQ,CAAC,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QACzF,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEZ,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["'use client'\n\n/**\n * `IntentPrefetch`: prefetches an in-content card link when a visitor shows intent, not when it scrolls into view.\n * Links with `prefetch=\"intent\"` (ButtonLink, TextLink, QuoteButton) render `prefetch={false}` and\n * `data-prefetch=\"intent\"`; this island holds one document listener for pointer entry, focus and touch, and calls\n * `router.prefetch(href)` for the one link the visitor points at, focuses or touches, once per page address. A touch\n * prefetches only the touched link, never the list around it; the current page and links to other sites are never\n * prefetched.\n *\n * Mount it once, in the root layout, beside the header:\n *\n * // src/app/layout.tsx\n * import { IntentPrefetch } from '@brandlab-digital/site-next/ui-kit/shell'\n * <body>\n * <SiteHeader … />\n * <IntentPrefetch />\n * …\n * </body>\n *\n * It renders nothing; without scripts the links still work and simply do not prefetch.\n */\nimport { useRouter } from 'next/navigation'\nimport { useEffect } from 'react'\nimport { INTENT_PREFETCH_SELECTOR } from '../prefetch.js'\n\nconst INTENT_EVENTS = ['pointerenter', 'focusin', 'touchstart'] as const\n\n/** The site path an intent link points to, without its fragment; null for another site or no href. */\nexport function intentPrefetchTarget(href: string | null): string | null {\n if (!href || !href.startsWith('/') || href.startsWith('//')) return null\n const target = href.split('#', 1)[0] ?? ''\n return target || null\n}\n\nfunction samePage(target: string, location: Location) {\n const path = (value: string) => value.replace(/\\/+$/, '') || '/'\n return path(target.split('?', 1)[0] ?? '') === path(location.pathname)\n}\n\nexport function IntentPrefetch(): null {\n const router = useRouter()\n\n useEffect(() => {\n const prefetched = new Set<string>()\n const onIntent = (event: Event) => {\n const element = event.target instanceof Element ? event.target : null\n const link = element?.closest<HTMLAnchorElement>(INTENT_PREFETCH_SELECTOR)\n const target = intentPrefetchTarget(link?.getAttribute('href') ?? null)\n if (!target || prefetched.has(target) || samePage(target, window.location)) return\n prefetched.add(target)\n router.prefetch(target)\n }\n // Capture, so pointerenter (which does not bubble) reaches the document; passive, so touch never waits on it.\n const options: AddEventListenerOptions = { capture: true, passive: true }\n for (const type of INTENT_EVENTS) document.addEventListener(type, onIntent, options)\n return () => {\n for (const type of INTENT_EVENTS) document.removeEventListener(type, onIntent, options)\n }\n }, [router])\n\n return null\n}\n"]}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* F4: the phone and compact-band menu (server component). A modal dialog named "Menu", sliding in at
|
|
3
|
-
* `min(100%, 420px)`: the close button first, then the
|
|
4
|
-
*
|
|
3
|
+
* `min(100%, 420px)`: the close button first, then the menu in the desktop order (F1, 3.14.0), each dropdown an
|
|
4
|
+
* accordion where it stands (one open at a time) and each run of plain links one list, then full-width Call and quote
|
|
5
|
+
* buttons, and the social links. Every link is in the server HTML. `SiteHeader` renders it;
|
|
5
6
|
* render it on its own only inside a recorded bespoke header.
|
|
6
7
|
*/
|
|
7
8
|
import type { BrandlabNavigation } from '@brandlab-digital/site-core/navigation';
|
|
8
9
|
import type { ReactNode } from 'react';
|
|
10
|
+
import type { RepeatedSlotOf } from '../repeated-slots.js';
|
|
9
11
|
import { type SkinEntry } from '../skin.js';
|
|
10
12
|
import type { UiPhone, UiSocial } from '../types.js';
|
|
11
13
|
export type MobileDrawerSlot = 'root' | 'menuButton' | 'menuLabel' | 'panel' | 'top' | 'close' | 'title' | 'logo' | 'nav' | 'group' | 'groupTrigger' | 'groupLinks' | 'groupLink' | 'links' | 'link' | 'actions' | 'call' | 'quote' | 'social' | 'socialLink';
|
|
@@ -15,7 +17,9 @@ export interface MobileDrawerLayout {
|
|
|
15
17
|
/** The side the panel slides from (`side` variant). */
|
|
16
18
|
readonly side?: 'end' | 'start';
|
|
17
19
|
}
|
|
18
|
-
|
|
20
|
+
/** Rendered once per group, link or social profile: styled from the skin stylesheet, never by class (3.14.0). */
|
|
21
|
+
export type MobileDrawerRepeatedSlot = RepeatedSlotOf<'mobileDrawer'>;
|
|
22
|
+
export type MobileDrawerSkin = SkinEntry<MobileDrawerSlot, MobileDrawerVariant, MobileDrawerLayout, MobileDrawerRepeatedSlot>;
|
|
19
23
|
/** The quote action. `href` is the quote form it falls back to without scripts, for example `/contact#quote`. */
|
|
20
24
|
export interface ShellQuote {
|
|
21
25
|
readonly label: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobile-drawer.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/shell/mobile-drawer.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mobile-drawer.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/shell/mobile-drawer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAA4C,MAAM,wCAAwC,CAAA;AAC1H,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAKpD,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,YAAY,GACZ,WAAW,GACX,OAAO,GACP,KAAK,GACL,OAAO,GACP,OAAO,GACP,MAAM,GACN,KAAK,GACL,OAAO,GACP,cAAc,GACd,YAAY,GACZ,WAAW,GACX,OAAO,GACP,MAAM,GACN,SAAS,GACT,MAAM,GACN,OAAO,GACP,QAAQ,GACR,YAAY,CAAA;AAChB,eAAO,MAAM,sBAAsB,2BAA4B,CAAA;AAC/D,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAA;AACzE,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,GAAG,OAAO,CAAA;CAChC;AACD,iHAAiH;AACjH,MAAM,MAAM,wBAAwB,GAAG,cAAc,CAAC,cAAc,CAAC,CAAA;AACrE,MAAM,MAAM,gBAAgB,GAAG,SAAS,CAAC,gBAAgB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,wBAAwB,CAAC,CAAA;AAE7H,iHAAiH;AACjH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACzD,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAA;IACvC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,sEAAsE;IACtE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,CAAA;IAClD,mEAAmE;IACnE,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED,wBAAgB,YAAY,CAAC,EAC3B,UAAU,EACV,KAAK,EACL,UAAU,EACV,KAAK,EACL,OAAY,EACZ,IAAI,EACJ,QAAsB,EACtB,OAAgB,EAChB,MAAM,EACN,OAAO,GACR,EAAE,iBAAiB,2CAwDnB"}
|
|
@@ -2,15 +2,27 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Icon } from '../icons.js';
|
|
3
3
|
import { PhoneLink } from '../primitives/links.js';
|
|
4
4
|
import { QuoteButton } from '../primitives/quote-button.js';
|
|
5
|
-
import { slot } from '../skin.js';
|
|
5
|
+
import { repeatedSlot, slot } from '../skin.js';
|
|
6
6
|
import { CurrentLink } from './current-link.js';
|
|
7
7
|
import { DrawerController } from './drawer-client.js';
|
|
8
8
|
import { SocialLinks } from './social-links.js';
|
|
9
9
|
export const MOBILE_DRAWER_VARIANTS = ['side', 'full'];
|
|
10
10
|
export function MobileDrawer({ navigation, phone, phoneLabel, quote, socials = [], logo, idPrefix = 'bl-header', variant = 'side', layout, classes, }) {
|
|
11
11
|
const id = `${idPrefix}-drawer`;
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// The drawer keeps the desktop menu's order (F1): a dropdown becomes an accordion where it stands, and each run of
|
|
13
|
+
// plain links between dropdowns is one list.
|
|
14
|
+
const runs = [];
|
|
15
|
+
for (const item of navigation.items) {
|
|
16
|
+
if (item.kind === 'menu')
|
|
17
|
+
runs.push(item);
|
|
18
|
+
else {
|
|
19
|
+
const last = runs[runs.length - 1];
|
|
20
|
+
if (Array.isArray(last))
|
|
21
|
+
last.push(item);
|
|
22
|
+
else
|
|
23
|
+
runs.push([item]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
14
26
|
return (_jsxs(DrawerController, { id: id, variant: variant, side: layout?.side ?? 'end', classes: {
|
|
15
27
|
root: classes?.root,
|
|
16
28
|
menuButton: classes?.menuButton,
|
|
@@ -20,6 +32,10 @@ export function MobileDrawer({ navigation, phone, phoneLabel, quote, socials = [
|
|
|
20
32
|
close: classes?.close,
|
|
21
33
|
title: classes?.title,
|
|
22
34
|
logo: classes?.logo,
|
|
23
|
-
}, logo: logo, menuIcon: _jsx(Icon, { name: "menu" }), closeIcon: _jsx(Icon, { name: "close" }), children: [
|
|
35
|
+
}, logo: logo, menuIcon: _jsx(Icon, { name: "menu" }), closeIcon: _jsx(Icon, { name: "close" }), children: [_jsx("nav", { "aria-label": "Main", ...slot(classes, 'nav'), children: runs.map((run) => Array.isArray(run) ? (_jsx("ul", { ...slot(classes, 'links'), children: run.map((link) => (_jsx("li", { children: _jsx(CurrentLink, { href: link.href, slot: "link", prefetch: false, children: link.label }) }, link.id))) }, `links-${run[0].id}`)) : (_jsx(GroupAccordion, { group: run, name: `${id}-groups` }, run.id))) }), _jsxs("div", { ...slot(classes, 'actions'), children: [_jsx(PhoneLink, { phone: phone, placement: "header", label: phoneLabel, showIcon: true, variant: "button", classes: { root: classes?.call } }), _jsx(QuoteButton, { label: quote.label, href: quote.href, placement: "navigation", ctaId: "quote-menu", classes: { root: classes?.quote } })] }), _jsx(SocialLinks, { socials: socials, slotName: "social", className: classes?.social })] }));
|
|
36
|
+
}
|
|
37
|
+
/** One dropdown as an accordion: its hub link first, the featured card's page, then its links. */
|
|
38
|
+
function GroupAccordion({ group, name }) {
|
|
39
|
+
return (_jsxs("details", { name: name, ...repeatedSlot('group'), children: [_jsxs("summary", { ...repeatedSlot('groupTrigger'), children: [_jsx("span", { children: group.label }), _jsx(Icon, { name: "chevron" })] }), _jsxs("ul", { ...repeatedSlot('groupLinks'), children: [_jsx("li", { children: _jsx(CurrentLink, { href: group.href, slot: "groupLink", prefetch: false, hub: true, children: group.hubLabel }) }), group.featured && !group.links.some((link) => link.href === group.featured?.href) ? (_jsx("li", { children: _jsx(CurrentLink, { href: group.featured.href, slot: "groupLink", prefetch: false, children: group.featured.label }) })) : null, group.links.map((link) => (_jsx("li", { children: _jsx(CurrentLink, { href: link.href, slot: "groupLink", prefetch: false, children: link.label }) }, link.href)))] })] }));
|
|
24
40
|
}
|
|
25
41
|
//# sourceMappingURL=mobile-drawer.js.map
|