@brandlab-digital/site-next 3.13.0 → 3.15.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 +79 -0
- package/dist/analytics.d.ts +1 -1
- package/dist/analytics.d.ts.map +1 -1
- package/dist/analytics.js +2 -0
- package/dist/analytics.js.map +1 -1
- 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/navigation/constants.d.ts +11 -2
- package/dist/navigation/constants.d.ts.map +1 -1
- package/dist/navigation/constants.js +11 -2
- package/dist/navigation/constants.js.map +1 -1
- package/dist/navigation/index.d.ts +1 -1
- package/dist/navigation/index.d.ts.map +1 -1
- package/dist/navigation/index.js +1 -1
- package/dist/navigation/index.js.map +1 -1
- package/dist/navigation/use-back-to-top.d.ts.map +1 -1
- package/dist/navigation/use-back-to-top.js +12 -4
- package/dist/navigation/use-back-to-top.js.map +1 -1
- package/dist/navigation/use-header-metrics.d.ts +1 -1
- package/dist/navigation/use-header-metrics.js +1 -1
- package/dist/navigation/use-header-metrics.js.map +1 -1
- package/dist/navigation/use-sticky-call-bar.d.ts +2 -1
- package/dist/navigation/use-sticky-call-bar.d.ts.map +1 -1
- package/dist/navigation/use-sticky-call-bar.js +11 -2
- package/dist/navigation/use-sticky-call-bar.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/ui-kit/brandlab-skin.d.ts +50 -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 +19 -10
- package/dist/ui-kit/prefetch.d.ts.map +1 -1
- package/dist/ui-kit/prefetch.js +23 -10
- package/dist/ui-kit/prefetch.js.map +1 -1
- 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 +11 -2
- package/dist/ui-kit/primitives/index.d.ts.map +1 -1
- package/dist/ui-kit/primitives/index.js +11 -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 +15 -0
- package/dist/ui-kit/primitives/links.d.ts.map +1 -1
- package/dist/ui-kit/primitives/links.js +6 -2
- package/dist/ui-kit/primitives/links.js.map +1 -1
- package/dist/ui-kit/primitives/lists.d.ts +51 -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 +25 -2
- package/dist/ui-kit/primitives/quote-button.d.ts.map +1 -1
- package/dist/ui-kit/primitives/quote-button.js +25 -3
- 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 +153 -0
- package/dist/ui-kit/repeated-slots.d.ts.map +1 -0
- package/dist/ui-kit/repeated-slots.js +97 -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/area-map.d.ts +64 -0
- package/dist/ui-kit/sections/area-map.d.ts.map +1 -0
- package/dist/ui-kit/sections/area-map.js +142 -0
- package/dist/ui-kit/sections/area-map.js.map +1 -0
- package/dist/ui-kit/sections/before-after.d.ts +59 -0
- package/dist/ui-kit/sections/before-after.d.ts.map +1 -0
- package/dist/ui-kit/sections/before-after.js +42 -0
- package/dist/ui-kit/sections/before-after.js.map +1 -0
- 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/council-rules.d.ts +64 -0
- package/dist/ui-kit/sections/council-rules.d.ts.map +1 -0
- package/dist/ui-kit/sections/council-rules.js +46 -0
- package/dist/ui-kit/sections/council-rules.js.map +1 -0
- package/dist/ui-kit/sections/coverage.d.ts +53 -0
- package/dist/ui-kit/sections/coverage.d.ts.map +1 -0
- package/dist/ui-kit/sections/coverage.js +57 -0
- package/dist/ui-kit/sections/coverage.js.map +1 -0
- package/dist/ui-kit/sections/emergency-band.d.ts +60 -0
- package/dist/ui-kit/sections/emergency-band.d.ts.map +1 -0
- package/dist/ui-kit/sections/emergency-band.js +43 -0
- package/dist/ui-kit/sections/emergency-band.js.map +1 -0
- 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/gallery.d.ts +60 -0
- package/dist/ui-kit/sections/gallery.d.ts.map +1 -0
- package/dist/ui-kit/sections/gallery.js +39 -0
- package/dist/ui-kit/sections/gallery.js.map +1 -0
- package/dist/ui-kit/sections/hero.d.ts +118 -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 +16 -1
- package/dist/ui-kit/sections/index.d.ts.map +1 -1
- package/dist/ui-kit/sections/index.js +16 -1
- package/dist/ui-kit/sections/index.js.map +1 -1
- package/dist/ui-kit/sections/licences.d.ts +72 -0
- package/dist/ui-kit/sections/licences.d.ts.map +1 -0
- package/dist/ui-kit/sections/licences.js +46 -0
- package/dist/ui-kit/sections/licences.js.map +1 -0
- package/dist/ui-kit/sections/local-facts.d.ts +78 -0
- package/dist/ui-kit/sections/local-facts.d.ts.map +1 -0
- package/dist/ui-kit/sections/local-facts.js +32 -0
- package/dist/ui-kit/sections/local-facts.js.map +1 -0
- package/dist/ui-kit/sections/nearby-areas.d.ts +50 -0
- package/dist/ui-kit/sections/nearby-areas.d.ts.map +1 -0
- package/dist/ui-kit/sections/nearby-areas.js +49 -0
- package/dist/ui-kit/sections/nearby-areas.js.map +1 -0
- package/dist/ui-kit/sections/nearby-jobs.d.ts +66 -0
- package/dist/ui-kit/sections/nearby-jobs.d.ts.map +1 -0
- package/dist/ui-kit/sections/nearby-jobs.js +49 -0
- package/dist/ui-kit/sections/nearby-jobs.js.map +1 -0
- 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/projects.d.ts +79 -0
- package/dist/ui-kit/sections/projects.d.ts.map +1 -0
- package/dist/ui-kit/sections/projects.js +69 -0
- package/dist/ui-kit/sections/projects.js.map +1 -0
- 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/quote-checklist.d.ts +43 -0
- package/dist/ui-kit/sections/quote-checklist.d.ts.map +1 -0
- package/dist/ui-kit/sections/quote-checklist.js +19 -0
- package/dist/ui-kit/sections/quote-checklist.js.map +1 -0
- package/dist/ui-kit/sections/related-services.d.ts +41 -0
- package/dist/ui-kit/sections/related-services.d.ts.map +1 -0
- package/dist/ui-kit/sections/related-services.js +52 -0
- package/dist/ui-kit/sections/related-services.js.map +1 -0
- package/dist/ui-kit/sections/reviews-band.d.ts +16 -2
- package/dist/ui-kit/sections/reviews-band.d.ts.map +1 -1
- package/dist/ui-kit/sections/reviews-band.js +13 -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 +6 -6
- package/dist/ui-kit/sections/site-footer.js.map +1 -1
- package/dist/ui-kit/sections/work-photos.d.ts +15 -0
- package/dist/ui-kit/sections/work-photos.d.ts.map +1 -0
- package/dist/ui-kit/sections/work-photos.js +24 -0
- package/dist/ui-kit/sections/work-photos.js.map +1 -0
- package/dist/ui-kit/shell/back-to-top.js +1 -1
- package/dist/ui-kit/shell/back-to-top.js.map +1 -1
- package/dist/ui-kit/shell/current-link.d.ts +21 -5
- package/dist/ui-kit/shell/current-link.d.ts.map +1 -1
- package/dist/ui-kit/shell/current-link.js +16 -5
- package/dist/ui-kit/shell/current-link.js.map +1 -1
- package/dist/ui-kit/shell/drawer-client.js +1 -1
- package/dist/ui-kit/shell/drawer-client.js.map +1 -1
- package/dist/ui-kit/shell/intent-prefetch.d.ts.map +1 -1
- package/dist/ui-kit/shell/intent-prefetch.js +3 -2
- package/dist/ui-kit/shell/intent-prefetch.js.map +1 -1
- package/dist/ui-kit/shell/mobile-call-bar.d.ts +6 -1
- package/dist/ui-kit/shell/mobile-call-bar.d.ts.map +1 -1
- package/dist/ui-kit/shell/mobile-call-bar.js +6 -3
- package/dist/ui-kit/shell/mobile-call-bar.js.map +1 -1
- 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 +21 -5
- package/dist/ui-kit/shell/mobile-drawer.js.map +1 -1
- package/dist/ui-kit/shell/shell-quote.d.ts +10 -0
- package/dist/ui-kit/shell/shell-quote.d.ts.map +1 -0
- package/dist/ui-kit/shell/shell-quote.js +13 -0
- package/dist/ui-kit/shell/shell-quote.js.map +1 -0
- package/dist/ui-kit/shell/site-header.d.ts +5 -2
- package/dist/ui-kit/shell/site-header.d.ts.map +1 -1
- package/dist/ui-kit/shell/site-header.js +9 -9
- 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 +793 -10
- package/package.json +6 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/navigation/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,0GAA0G;AAC1G,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAA;AAC1C,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAA;AAC3C,sEAAsE;AACtE,MAAM,CAAC,MAAM,qBAAqB,GAAG,oCAAoC,CAAA;AAEzE
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/navigation/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,0GAA0G;AAC1G,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAA;AAC1C,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAA;AAC3C,sEAAsE;AACtE,MAAM,CAAC,MAAM,qBAAqB,GAAG,oCAAoC,CAAA;AAEzE;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAA;AAC5C,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAA;AAC5C,0FAA0F;AAC1F,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,CAAA;AAIhD,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,IAAI,KAAK,IAAI,wBAAwB;QAAE,OAAO,SAAS,CAAA;IACvD,IAAI,KAAK,IAAI,wBAAwB;QAAE,OAAO,SAAS,CAAA;IACvD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,wDAAwD;AACxD,MAAM,CAAC,MAAM,oBAAoB,GAAG,wBAAwB,CAAA;AAC5D;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,iBAAiB,CAAA;AACzD,MAAM,CAAC,MAAM,oBAAoB,GAAG,YAAY,CAAA;AAChD,6FAA6F;AAC7F,MAAM,CAAC,MAAM,kBAAkB,GAAG,qBAAqB,CAAA;AAEvD,kEAAkE;AAClE,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAA;AAC5C,iEAAiE;AACjE,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAA","sourcesContent":["/**\n * The shell's timings, breakpoints and hooks as plain values (no React), so server components can read them. A site\n * can't change them.\n */\n\n/** F3: a fine mouse opens a panel 100 ms after it enters an item and closes it 200 ms after it leaves. */\nexport const NAV_HOVER_OPEN_DELAY_MS = 100\nexport const NAV_HOVER_CLOSE_DELAY_MS = 200\n/** Only a fine mouse pointer that can hover opens panels on hover. */\nexport const NAV_HOVER_MEDIA_QUERY = '(hover: hover) and (pointer: fine)'\n\n/**\n * F2: header modes by the header's own width. The full menu starts at 1200 px (3.15.0; 1280 before), so a 1280 px\n * window with a classic scrollbar (a 1265 px header) shows the menu, not the Menu button.\n */\nexport const HEADER_DESKTOP_MIN_WIDTH = 1200\nexport const HEADER_COMPACT_MIN_WIDTH = 1024\n/** With `compactAfterScroll`, the header compacts once the page has scrolled this far. */\nexport const HEADER_COMPACT_AFTER_SCROLL_PX = 80\n\nexport type HeaderMode = 'phone' | 'compact' | 'desktop'\n\nexport function headerModeFor(width: number): HeaderMode {\n if (width >= HEADER_DESKTOP_MIN_WIDTH) return 'desktop'\n if (width >= HEADER_COMPACT_MIN_WIDTH) return 'compact'\n return 'phone'\n}\n\n/** F6: the call bar shows below this viewport width. */\nexport const CALL_BAR_MEDIA_QUERY = '(max-width: 1023.98px)'\n/**\n * F6: while the call bar is mounted, the root element carries this attribute (`shown` or `hidden`) and the document gets\n * a `CALL_BAR_STATE_EVENT` (`detail.shown`) on every change, so back-to-top stays hidden while the bar shows (3.15.0).\n */\nexport const CALL_BAR_STATE_ATTRIBUTE = 'data-bl-callbar'\nexport const CALL_BAR_STATE_EVENT = 'bl:callbar'\n/** F6: the hero's own call and quote buttons (and any other call path) carry this marker. */\nexport const CALL_PATH_SELECTOR = '[data-bl-call-path]'\n\n/** F7: back-to-top shows after this many screens of scrolling. */\nexport const BACK_TO_TOP_AFTER_SCREENS = 1.5\n/** F12: the skip-link target, which back-to-top also focuses. */\nexport const MAIN_CONTENT_ID = 'main'\n"]}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* The ui-kit components use the hooks; a site uses them directly only in a recorded bespoke header. The hook modules
|
|
4
4
|
* are client modules; the constants are plain values a server component can read.
|
|
5
5
|
*/
|
|
6
|
-
export { BACK_TO_TOP_AFTER_SCREENS, CALL_BAR_MEDIA_QUERY, CALL_PATH_SELECTOR, HEADER_COMPACT_AFTER_SCROLL_PX, HEADER_COMPACT_MIN_WIDTH, HEADER_DESKTOP_MIN_WIDTH, MAIN_CONTENT_ID, NAV_HOVER_CLOSE_DELAY_MS, NAV_HOVER_MEDIA_QUERY, NAV_HOVER_OPEN_DELAY_MS, headerModeFor, type HeaderMode, } from './constants.js';
|
|
6
|
+
export { BACK_TO_TOP_AFTER_SCREENS, CALL_BAR_MEDIA_QUERY, CALL_BAR_STATE_ATTRIBUTE, CALL_BAR_STATE_EVENT, CALL_PATH_SELECTOR, HEADER_COMPACT_AFTER_SCROLL_PX, HEADER_COMPACT_MIN_WIDTH, HEADER_DESKTOP_MIN_WIDTH, MAIN_CONTENT_ID, NAV_HOVER_CLOSE_DELAY_MS, NAV_HOVER_MEDIA_QUERY, NAV_HOVER_OPEN_DELAY_MS, headerModeFor, type HeaderMode, } from './constants.js';
|
|
7
7
|
export { useNavigationMenu, type NavigationMenu, type NavigationOpenedBy } from './use-navigation-menu.js';
|
|
8
8
|
export { useHeaderMetrics, type HeaderMetrics } from './use-header-metrics.js';
|
|
9
9
|
export { useStickyCallBar } from './use-sticky-call-bar.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/navigation/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,kBAAkB,EAClB,8BAA8B,EAC9B,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,EACb,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,KAAK,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAC1G,OAAO,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/navigation/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,8BAA8B,EAC9B,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,EACb,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,KAAK,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAC1G,OAAO,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/navigation/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* The ui-kit components use the hooks; a site uses them directly only in a recorded bespoke header. The hook modules
|
|
4
4
|
* are client modules; the constants are plain values a server component can read.
|
|
5
5
|
*/
|
|
6
|
-
export { BACK_TO_TOP_AFTER_SCREENS, CALL_BAR_MEDIA_QUERY, CALL_PATH_SELECTOR, HEADER_COMPACT_AFTER_SCROLL_PX, HEADER_COMPACT_MIN_WIDTH, HEADER_DESKTOP_MIN_WIDTH, MAIN_CONTENT_ID, NAV_HOVER_CLOSE_DELAY_MS, NAV_HOVER_MEDIA_QUERY, NAV_HOVER_OPEN_DELAY_MS, headerModeFor, } from './constants.js';
|
|
6
|
+
export { BACK_TO_TOP_AFTER_SCREENS, CALL_BAR_MEDIA_QUERY, CALL_BAR_STATE_ATTRIBUTE, CALL_BAR_STATE_EVENT, CALL_PATH_SELECTOR, HEADER_COMPACT_AFTER_SCROLL_PX, HEADER_COMPACT_MIN_WIDTH, HEADER_DESKTOP_MIN_WIDTH, MAIN_CONTENT_ID, NAV_HOVER_CLOSE_DELAY_MS, NAV_HOVER_MEDIA_QUERY, NAV_HOVER_OPEN_DELAY_MS, headerModeFor, } from './constants.js';
|
|
7
7
|
export { useNavigationMenu } from './use-navigation-menu.js';
|
|
8
8
|
export { useHeaderMetrics } from './use-header-metrics.js';
|
|
9
9
|
export { useStickyCallBar } from './use-sticky-call-bar.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/navigation/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,kBAAkB,EAClB,8BAA8B,EAC9B,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,GAEd,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAgD,MAAM,0BAA0B,CAAA;AAC1G,OAAO,EAAE,gBAAgB,EAAsB,MAAM,yBAAyB,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA","sourcesContent":["/**\n * `@brandlab-digital/site-next/navigation`: the client hooks behind the ui-kit shell, and its timings and breakpoints.\n * The ui-kit components use the hooks; a site uses them directly only in a recorded bespoke header. The hook modules\n * are client modules; the constants are plain values a server component can read.\n */\nexport {\n BACK_TO_TOP_AFTER_SCREENS,\n CALL_BAR_MEDIA_QUERY,\n CALL_PATH_SELECTOR,\n HEADER_COMPACT_AFTER_SCROLL_PX,\n HEADER_COMPACT_MIN_WIDTH,\n HEADER_DESKTOP_MIN_WIDTH,\n MAIN_CONTENT_ID,\n NAV_HOVER_CLOSE_DELAY_MS,\n NAV_HOVER_MEDIA_QUERY,\n NAV_HOVER_OPEN_DELAY_MS,\n headerModeFor,\n type HeaderMode,\n} from './constants.js'\nexport { useNavigationMenu, type NavigationMenu, type NavigationOpenedBy } from './use-navigation-menu.js'\nexport { useHeaderMetrics, type HeaderMetrics } from './use-header-metrics.js'\nexport { useStickyCallBar } from './use-sticky-call-bar.js'\nexport { useBackToTop } from './use-back-to-top.js'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/navigation/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,8BAA8B,EAC9B,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,GAEd,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAgD,MAAM,0BAA0B,CAAA;AAC1G,OAAO,EAAE,gBAAgB,EAAsB,MAAM,yBAAyB,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA","sourcesContent":["/**\n * `@brandlab-digital/site-next/navigation`: the client hooks behind the ui-kit shell, and its timings and breakpoints.\n * The ui-kit components use the hooks; a site uses them directly only in a recorded bespoke header. The hook modules\n * are client modules; the constants are plain values a server component can read.\n */\nexport {\n BACK_TO_TOP_AFTER_SCREENS,\n CALL_BAR_MEDIA_QUERY,\n CALL_BAR_STATE_ATTRIBUTE,\n CALL_BAR_STATE_EVENT,\n CALL_PATH_SELECTOR,\n HEADER_COMPACT_AFTER_SCROLL_PX,\n HEADER_COMPACT_MIN_WIDTH,\n HEADER_DESKTOP_MIN_WIDTH,\n MAIN_CONTENT_ID,\n NAV_HOVER_CLOSE_DELAY_MS,\n NAV_HOVER_MEDIA_QUERY,\n NAV_HOVER_OPEN_DELAY_MS,\n headerModeFor,\n type HeaderMode,\n} from './constants.js'\nexport { useNavigationMenu, type NavigationMenu, type NavigationOpenedBy } from './use-navigation-menu.js'\nexport { useHeaderMetrics, type HeaderMetrics } from './use-header-metrics.js'\nexport { useStickyCallBar } from './use-sticky-call-bar.js'\nexport { useBackToTop } from './use-back-to-top.js'\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-back-to-top.d.ts","sourceRoot":"","sources":["../../src/navigation/use-back-to-top.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-back-to-top.d.ts","sourceRoot":"","sources":["../../src/navigation/use-back-to-top.ts"],"names":[],"mappings":"AAWA,wBAAgB,YAAY,IAAI;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAAC,WAAW,IAAI,IAAI,CAAA;CAAE,CA8B/E"}
|
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
/**
|
|
3
3
|
* F7: back-to-top. It shows after one and a half screens of scrolling; it scrolls smoothly unless the visitor prefers
|
|
4
|
-
* reduced motion, and moves focus to the skip-link target (`#main`), so keyboard users start again at the top.
|
|
4
|
+
* reduced motion, and moves focus to the skip-link target (`#main`), so keyboard users start again at the top. It stays
|
|
5
|
+
* hidden while the phone call bar shows (3.15.0): stacked above the bar it covered the page's content on a phone, and
|
|
6
|
+
* the bar already sits within one tap. It shows again once the bar hides (over the footer, for example).
|
|
5
7
|
*/
|
|
6
8
|
import { useCallback, useEffect, useState } from 'react';
|
|
7
|
-
import { BACK_TO_TOP_AFTER_SCREENS, MAIN_CONTENT_ID } from './constants.js';
|
|
9
|
+
import { BACK_TO_TOP_AFTER_SCREENS, CALL_BAR_STATE_ATTRIBUTE, CALL_BAR_STATE_EVENT, MAIN_CONTENT_ID } from './constants.js';
|
|
8
10
|
export function useBackToTop() {
|
|
9
|
-
const [
|
|
11
|
+
const [scrolled, setScrolled] = useState(false);
|
|
12
|
+
const [callBarShown, setCallBarShown] = useState(false);
|
|
10
13
|
useEffect(() => {
|
|
11
|
-
const update = () =>
|
|
14
|
+
const update = () => setScrolled(window.scrollY > window.innerHeight * BACK_TO_TOP_AFTER_SCREENS);
|
|
15
|
+
const onCallBar = (event) => setCallBarShown(event.detail?.shown === true);
|
|
12
16
|
update();
|
|
17
|
+
setCallBarShown(document.documentElement.getAttribute(CALL_BAR_STATE_ATTRIBUTE) === 'shown');
|
|
13
18
|
window.addEventListener('scroll', update, { passive: true });
|
|
14
19
|
window.addEventListener('resize', update);
|
|
20
|
+
document.addEventListener(CALL_BAR_STATE_EVENT, onCallBar);
|
|
15
21
|
return () => {
|
|
16
22
|
window.removeEventListener('scroll', update);
|
|
17
23
|
window.removeEventListener('resize', update);
|
|
24
|
+
document.removeEventListener(CALL_BAR_STATE_EVENT, onCallBar);
|
|
18
25
|
};
|
|
19
26
|
}, []);
|
|
27
|
+
const shown = scrolled && !callBarShown;
|
|
20
28
|
const scrollToTop = useCallback(() => {
|
|
21
29
|
const reduced = typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
22
30
|
window.scrollTo({ top: 0, behavior: reduced ? 'auto' : 'smooth' });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-back-to-top.js","sourceRoot":"","sources":["../../src/navigation/use-back-to-top.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ
|
|
1
|
+
{"version":3,"file":"use-back-to-top.js","sourceRoot":"","sources":["../../src/navigation/use-back-to-top.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACxD,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAE3H,MAAM,UAAU,YAAY;IAC1B,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/C,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAEvD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,WAAW,GAAG,yBAAyB,CAAC,CAAA;QACjG,MAAM,SAAS,GAAG,CAAC,KAAY,EAAE,EAAE,CAAC,eAAe,CAAE,KAA0C,CAAC,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC,CAAA;QACvH,MAAM,EAAE,CAAA;QACR,eAAe,CAAC,QAAQ,CAAC,eAAe,CAAC,YAAY,CAAC,wBAAwB,CAAC,KAAK,OAAO,CAAC,CAAA;QAC5F,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QAC5D,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACzC,QAAQ,CAAC,gBAAgB,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAA;QAC1D,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YAC5C,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YAC5C,QAAQ,CAAC,mBAAmB,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAA;QAC/D,CAAC,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IACN,MAAM,KAAK,GAAG,QAAQ,IAAI,CAAC,YAAY,CAAA;IAEvC,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,MAAM,OAAO,GAAG,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC,kCAAkC,CAAC,CAAC,OAAO,CAAA;QACxH,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAA;QAClE,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAC,CAAA;QACrD,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC;YAAE,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;QACvE,IAAI,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IACrC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAA;AAC/B,CAAC","sourcesContent":["'use client'\n\n/**\n * F7: back-to-top. It shows after one and a half screens of scrolling; it scrolls smoothly unless the visitor prefers\n * reduced motion, and moves focus to the skip-link target (`#main`), so keyboard users start again at the top. It stays\n * hidden while the phone call bar shows (3.15.0): stacked above the bar it covered the page's content on a phone, and\n * the bar already sits within one tap. It shows again once the bar hides (over the footer, for example).\n */\nimport { useCallback, useEffect, useState } from 'react'\nimport { BACK_TO_TOP_AFTER_SCREENS, CALL_BAR_STATE_ATTRIBUTE, CALL_BAR_STATE_EVENT, MAIN_CONTENT_ID } from './constants.js'\n\nexport function useBackToTop(): { readonly shown: boolean; scrollToTop(): void } {\n const [scrolled, setScrolled] = useState(false)\n const [callBarShown, setCallBarShown] = useState(false)\n\n useEffect(() => {\n const update = () => setScrolled(window.scrollY > window.innerHeight * BACK_TO_TOP_AFTER_SCREENS)\n const onCallBar = (event: Event) => setCallBarShown((event as CustomEvent<{ shown?: boolean }>).detail?.shown === true)\n update()\n setCallBarShown(document.documentElement.getAttribute(CALL_BAR_STATE_ATTRIBUTE) === 'shown')\n window.addEventListener('scroll', update, { passive: true })\n window.addEventListener('resize', update)\n document.addEventListener(CALL_BAR_STATE_EVENT, onCallBar)\n return () => {\n window.removeEventListener('scroll', update)\n window.removeEventListener('resize', update)\n document.removeEventListener(CALL_BAR_STATE_EVENT, onCallBar)\n }\n }, [])\n const shown = scrolled && !callBarShown\n\n const scrollToTop = useCallback(() => {\n const reduced = typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches\n window.scrollTo({ top: 0, behavior: reduced ? 'auto' : 'smooth' })\n const main = document.getElementById(MAIN_CONTENT_ID)\n if (!main) return\n if (!main.hasAttribute('tabindex')) main.setAttribute('tabindex', '-1')\n main.focus({ preventScroll: true })\n }, [])\n\n return { shown, scrollToTop }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* F2: the header's measured state. The mode follows the header's own width (so a framed specimen behaves like a real
|
|
3
|
-
* window):
|
|
3
|
+
* window): 1200 px and up `desktop`, 1024 to 1199 px `compact`, below 1024 px `phone`. The hook writes the frozen
|
|
4
4
|
* hooks on the header root (`data-mode`, `data-hydrated`, `data-compact`) and `--bl-header-height` on the document,
|
|
5
5
|
* which drives `scroll-padding-top` (WCAG 2.4.11).
|
|
6
6
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
/**
|
|
3
3
|
* F2: the header's measured state. The mode follows the header's own width (so a framed specimen behaves like a real
|
|
4
|
-
* window):
|
|
4
|
+
* window): 1200 px and up `desktop`, 1024 to 1199 px `compact`, below 1024 px `phone`. The hook writes the frozen
|
|
5
5
|
* hooks on the header root (`data-mode`, `data-hydrated`, `data-compact`) and `--bl-header-height` on the document,
|
|
6
6
|
* which drives `scroll-padding-top` (WCAG 2.4.11).
|
|
7
7
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-header-metrics.js","sourceRoot":"","sources":["../../src/navigation/use-header-metrics.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAA;AAC3D,OAAO,EAAE,8BAA8B,EAAE,aAAa,EAAmB,MAAM,gBAAgB,CAAA;AAQ/F,MAAM,UAAU,gBAAgB,CAAC,OAAsC,EAAE,UAA4C,EAAE;IACrH,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,IAAI,KAAK,CAAA;IAC9D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAA;IAEtF,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAA;QAC5B,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,MAAM,IAAI,GAAG,QAAQ,CAAC,eAAe,CAAA;QACrC,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,MAAM,GAAG,GAAG,IAAI,CAAC,qBAAqB,EAAE,CAAA;YACxC,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YACrC,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAA;YACxB,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAC3E,UAAU,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QAC3G,CAAC,CAAA;QACD,MAAM,QAAQ,GAAG,GAAG,EAAE;YACpB,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,GAAG,8BAA8B,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;QAC3F,CAAC,CAAA;QACD,IAAI,CAAC,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAA;QAC9B,OAAO,EAAE,CAAA;QACT,IAAI,QAAoC,CAAA;QACxC,IAAI,OAAO,cAAc,KAAK,UAAU,EAAE,CAAC;YACzC,QAAQ,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAA;YACtC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAC5C,CAAC;QACD,IAAI,kBAAkB,EAAE,CAAC;YACvB,QAAQ,EAAE,CAAA;YACV,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QAChE,CAAC;QACD,OAAO,GAAG,EAAE;YACV,QAAQ,EAAE,UAAU,EAAE,CAAA;YACtB,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;YAC7C,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;YAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAA;YAC3B,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,oBAAoB,CAAC,CAAA;QACjD,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAA;IAEjC,OAAO,OAAO,CAAA;AAChB,CAAC","sourcesContent":["'use client'\n\n/**\n * F2: the header's measured state. The mode follows the header's own width (so a framed specimen behaves like a real\n * window):
|
|
1
|
+
{"version":3,"file":"use-header-metrics.js","sourceRoot":"","sources":["../../src/navigation/use-header-metrics.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAA;AAC3D,OAAO,EAAE,8BAA8B,EAAE,aAAa,EAAmB,MAAM,gBAAgB,CAAA;AAQ/F,MAAM,UAAU,gBAAgB,CAAC,OAAsC,EAAE,UAA4C,EAAE;IACrH,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,IAAI,KAAK,CAAA;IAC9D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAA;IAEtF,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAA;QAC5B,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,MAAM,IAAI,GAAG,QAAQ,CAAC,eAAe,CAAA;QACrC,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,MAAM,GAAG,GAAG,IAAI,CAAC,qBAAqB,EAAE,CAAA;YACxC,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YACrC,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAA;YACxB,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAC3E,UAAU,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QAC3G,CAAC,CAAA;QACD,MAAM,QAAQ,GAAG,GAAG,EAAE;YACpB,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,GAAG,8BAA8B,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;QAC3F,CAAC,CAAA;QACD,IAAI,CAAC,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAA;QAC9B,OAAO,EAAE,CAAA;QACT,IAAI,QAAoC,CAAA;QACxC,IAAI,OAAO,cAAc,KAAK,UAAU,EAAE,CAAC;YACzC,QAAQ,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAA;YACtC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAC5C,CAAC;QACD,IAAI,kBAAkB,EAAE,CAAC;YACvB,QAAQ,EAAE,CAAA;YACV,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QAChE,CAAC;QACD,OAAO,GAAG,EAAE;YACV,QAAQ,EAAE,UAAU,EAAE,CAAA;YACtB,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;YAC7C,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;YAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAA;YAC3B,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,oBAAoB,CAAC,CAAA;QACjD,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAA;IAEjC,OAAO,OAAO,CAAA;AAChB,CAAC","sourcesContent":["'use client'\n\n/**\n * F2: the header's measured state. The mode follows the header's own width (so a framed specimen behaves like a real\n * window): 1200 px and up `desktop`, 1024 to 1199 px `compact`, below 1024 px `phone`. The hook writes the frozen\n * hooks on the header root (`data-mode`, `data-hydrated`, `data-compact`) and `--bl-header-height` on the document,\n * which drives `scroll-padding-top` (WCAG 2.4.11).\n */\nimport { useEffect, useState, type RefObject } from 'react'\nimport { HEADER_COMPACT_AFTER_SCROLL_PX, headerModeFor, type HeaderMode } from './constants.js'\n\nexport interface HeaderMetrics {\n /** Null until the header has been measured after hydration. */\n readonly mode: HeaderMode | null\n readonly hydrated: boolean\n}\n\nexport function useHeaderMetrics(rootRef: RefObject<HTMLElement | null>, options: { compactAfterScroll?: boolean } = {}): HeaderMetrics {\n const compactAfterScroll = options.compactAfterScroll ?? false\n const [metrics, setMetrics] = useState<HeaderMetrics>({ mode: null, hydrated: false })\n\n useEffect(() => {\n const root = rootRef.current\n if (!root) return\n const html = document.documentElement\n const measure = () => {\n const box = root.getBoundingClientRect()\n const mode = headerModeFor(box.width)\n root.dataset.mode = mode\n html.style.setProperty('--bl-header-height', `${Math.round(box.height)}px`)\n setMetrics((current) => (current.mode === mode && current.hydrated ? current : { mode, hydrated: true }))\n }\n const onScroll = () => {\n root.dataset.compact = window.scrollY > HEADER_COMPACT_AFTER_SCROLL_PX ? 'true' : 'false'\n }\n root.dataset.hydrated = 'true'\n measure()\n let observer: ResizeObserver | undefined\n if (typeof ResizeObserver === 'function') {\n observer = new ResizeObserver(measure)\n observer.observe(root)\n } else {\n window.addEventListener('resize', measure)\n }\n if (compactAfterScroll) {\n onScroll()\n window.addEventListener('scroll', onScroll, { passive: true })\n }\n return () => {\n observer?.disconnect()\n window.removeEventListener('resize', measure)\n window.removeEventListener('scroll', onScroll)\n delete root.dataset.compact\n html.style.removeProperty('--bl-header-height')\n }\n }, [rootRef, compactAfterScroll])\n\n return metrics\n}\n"]}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* screen (the hero's own call and quote buttons, marked `data-bl-call-path`) has scrolled out, and hides again while
|
|
4
4
|
* a call path, the page footer or an open dialog (the drawer, the quote dialog) is on screen, while a form field has
|
|
5
5
|
* focus (v2) and while focus is inside the footer or a call path. The hook keeps `--bl-callbar-height` at the bar's height while it shows and `0px` otherwise, which
|
|
6
|
-
* drives `scroll-padding-bottom` (WCAG 2.4.11) and
|
|
6
|
+
* drives `scroll-padding-bottom` (WCAG 2.4.11), and publishes its state (`data-bl-callbar` on the root element and a
|
|
7
|
+
* `bl:callbar` event) so back-to-top stays hidden while the bar shows: on a phone the button never covers the bar.
|
|
7
8
|
*/
|
|
8
9
|
import { type RefObject } from 'react';
|
|
9
10
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-sticky-call-bar.d.ts","sourceRoot":"","sources":["../../src/navigation/use-sticky-call-bar.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"use-sticky-call-bar.d.ts","sourceRoot":"","sources":["../../src/navigation/use-sticky-call-bar.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAyCjE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAmEnG"}
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
* screen (the hero's own call and quote buttons, marked `data-bl-call-path`) has scrolled out, and hides again while
|
|
5
5
|
* a call path, the page footer or an open dialog (the drawer, the quote dialog) is on screen, while a form field has
|
|
6
6
|
* focus (v2) and while focus is inside the footer or a call path. The hook keeps `--bl-callbar-height` at the bar's height while it shows and `0px` otherwise, which
|
|
7
|
-
* drives `scroll-padding-bottom` (WCAG 2.4.11) and
|
|
7
|
+
* drives `scroll-padding-bottom` (WCAG 2.4.11), and publishes its state (`data-bl-callbar` on the root element and a
|
|
8
|
+
* `bl:callbar` event) so back-to-top stays hidden while the bar shows: on a phone the button never covers the bar.
|
|
8
9
|
*/
|
|
9
10
|
import { useLayoutEffect, useState } from 'react';
|
|
10
|
-
import { CALL_BAR_MEDIA_QUERY, CALL_PATH_SELECTOR } from './constants.js';
|
|
11
|
+
import { CALL_BAR_MEDIA_QUERY, CALL_BAR_STATE_ATTRIBUTE, CALL_BAR_STATE_EVENT, CALL_PATH_SELECTOR } from './constants.js';
|
|
11
12
|
const FIELD_SELECTOR = [
|
|
12
13
|
'input:not([type="button"], [type="submit"], [type="reset"], [type="checkbox"], [type="radio"], [type="hidden"])',
|
|
13
14
|
'textarea',
|
|
@@ -58,10 +59,16 @@ export function useStickyCallBar(rootRef, resetKey) {
|
|
|
58
59
|
const onScreen = new Set();
|
|
59
60
|
let fieldFocused = isField(document.activeElement);
|
|
60
61
|
let focusHidden = inHideZone(document.activeElement);
|
|
62
|
+
let published;
|
|
61
63
|
const update = () => {
|
|
62
64
|
const next = (narrow?.matches ?? false) && onScreen.size === 0 && !fieldFocused && !focusHidden && !document.querySelector('dialog[open]');
|
|
63
65
|
setShown(next);
|
|
64
66
|
html.style.setProperty('--bl-callbar-height', next ? `${Math.round(root.getBoundingClientRect().height)}px` : '0px');
|
|
67
|
+
if (next === published)
|
|
68
|
+
return;
|
|
69
|
+
published = next;
|
|
70
|
+
html.setAttribute(CALL_BAR_STATE_ATTRIBUTE, next ? 'shown' : 'hidden');
|
|
71
|
+
document.dispatchEvent(new CustomEvent(CALL_BAR_STATE_EVENT, { detail: { shown: next } }));
|
|
65
72
|
};
|
|
66
73
|
let intersections;
|
|
67
74
|
if (typeof IntersectionObserver === 'function') {
|
|
@@ -103,6 +110,8 @@ export function useStickyCallBar(rootRef, resetKey) {
|
|
|
103
110
|
document.removeEventListener('focusout', onFocusOut);
|
|
104
111
|
narrow?.removeEventListener('change', update);
|
|
105
112
|
html.style.removeProperty('--bl-callbar-height');
|
|
113
|
+
html.removeAttribute(CALL_BAR_STATE_ATTRIBUTE);
|
|
114
|
+
document.dispatchEvent(new CustomEvent(CALL_BAR_STATE_EVENT, { detail: { shown: false } }));
|
|
106
115
|
};
|
|
107
116
|
}, [rootRef, resetKey]);
|
|
108
117
|
return shown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-sticky-call-bar.js","sourceRoot":"","sources":["../../src/navigation/use-sticky-call-bar.ts"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\n/**\n * F6: when the phone call bar shows. TotalCare's logic, ported: below 1024 px the bar shows once every call path on\n * screen (the hero's own call and quote buttons, marked `data-bl-call-path`) has scrolled out, and hides again while\n * a call path, the page footer or an open dialog (the drawer, the quote dialog) is on screen, while a form field has\n * focus (v2) and while focus is inside the footer or a call path. The hook keeps `--bl-callbar-height` at the bar's height while it shows and `0px` otherwise, which\n * drives `scroll-padding-bottom` (WCAG 2.4.11) and lifts back-to-top above the bar.\n */\nimport { useLayoutEffect, useState, type RefObject } from 'react'\nimport { CALL_BAR_MEDIA_QUERY, CALL_PATH_SELECTOR } from './constants.js'\n\nconst FIELD_SELECTOR = [\n 'input:not([type=\"button\"], [type=\"submit\"], [type=\"reset\"], [type=\"checkbox\"], [type=\"radio\"], [type=\"hidden\"])',\n 'textarea',\n 'select',\n '[contenteditable=\"\"]',\n '[contenteditable=\"true\"]',\n].join(', ')\n\nfunction isField(target: EventTarget | null): boolean {\n return target instanceof Element && target.matches(FIELD_SELECTOR)\n}\n\n/** The page footer: the shared footer, or a `<footer>` that isn't inside an article, aside, main, nav or section. */\nfunction isPageFooter(element: Element): boolean {\n return element.matches('[data-bl=\"footer\"]') || !element.parentElement?.closest('article, aside, main, nav, section')\n}\n\nfunction pageFooters(): Element[] {\n return Array.from(document.querySelectorAll('footer, [data-bl=\"footer\"]')).filter(isPageFooter)\n}\n\n/**\n * Focus inside the footer or a call path hides the bar at once, before the observer reports (fast Tab stops would\n * otherwise sit under the bar, K5).\n */\nfunction inHideZone(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false\n if (target.closest(CALL_PATH_SELECTOR)) return true\n const footer = target.closest('footer, [data-bl=\"footer\"]')\n return footer !== null && isPageFooter(footer)\n}\n\n/** True when any part of the element's box is inside the viewport. */\nfunction inViewport(element: Element): boolean {\n const box = element.getBoundingClientRect()\n return box.bottom > 0 && box.right > 0 && box.top < window.innerHeight && box.left < window.innerWidth\n}\n\n/**\n * True while the bar should show. `resetKey` (the route) re-collects the call paths and the footer. The first state\n * is seeded from their boxes before paint, so the bar never flashes on load or on a client navigation while the\n * observer's first report is pending.\n */\nexport function useStickyCallBar(rootRef: RefObject<HTMLElement | null>, resetKey: unknown): boolean {\n const [shown, setShown] = useState(false)\n\n useLayoutEffect(() => {\n const root = rootRef.current\n if (!root) return\n const html = document.documentElement\n const narrow = typeof window.matchMedia === 'function' ? window.matchMedia(CALL_BAR_MEDIA_QUERY) : null\n const onScreen = new Set<Element>()\n let fieldFocused = isField(document.activeElement)\n let focusHidden = inHideZone(document.activeElement)\n\n const update = () => {\n const next = (narrow?.matches ?? false) && onScreen.size === 0 && !fieldFocused && !focusHidden && !document.querySelector('dialog[open]')\n setShown(next)\n html.style.setProperty('--bl-callbar-height', next ? `${Math.round(root.getBoundingClientRect().height)}px` : '0px')\n }\n\n let intersections: IntersectionObserver | undefined\n if (typeof IntersectionObserver === 'function') {\n intersections = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) onScreen.add(entry.target)\n else onScreen.delete(entry.target)\n }\n update()\n })\n }\n for (const target of [...document.querySelectorAll(CALL_PATH_SELECTOR), ...pageFooters()]) {\n if (inViewport(target)) onScreen.add(target)\n intersections?.observe(target)\n }\n const dialogs = new MutationObserver(update)\n dialogs.observe(document.body, { subtree: true, attributes: true, attributeFilter: ['open'] })\n const onFocusIn = (event: FocusEvent) => {\n fieldFocused = isField(event.target)\n focusHidden = inHideZone(event.target)\n update()\n }\n const onFocusOut = (event: FocusEvent) => {\n fieldFocused = isField(event.relatedTarget)\n focusHidden = inHideZone(event.relatedTarget)\n update()\n }\n document.addEventListener('focusin', onFocusIn)\n document.addEventListener('focusout', onFocusOut)\n narrow?.addEventListener('change', update)\n update()\n\n return () => {\n intersections?.disconnect()\n dialogs.disconnect()\n document.removeEventListener('focusin', onFocusIn)\n document.removeEventListener('focusout', onFocusOut)\n narrow?.removeEventListener('change', update)\n html.style.removeProperty('--bl-callbar-height')\n }\n }, [rootRef, resetKey])\n\n return shown\n}\n"]}
|
|
1
|
+
{"version":3,"file":"use-sticky-call-bar.js","sourceRoot":"","sources":["../../src/navigation/use-sticky-call-bar.ts"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\n/**\n * F6: when the phone call bar shows. TotalCare's logic, ported: below 1024 px the bar shows once every call path on\n * screen (the hero's own call and quote buttons, marked `data-bl-call-path`) has scrolled out, and hides again while\n * a call path, the page footer or an open dialog (the drawer, the quote dialog) is on screen, while a form field has\n * focus (v2) and while focus is inside the footer or a call path. The hook keeps `--bl-callbar-height` at the bar's height while it shows and `0px` otherwise, which\n * drives `scroll-padding-bottom` (WCAG 2.4.11), and publishes its state (`data-bl-callbar` on the root element and a\n * `bl:callbar` event) so back-to-top stays hidden while the bar shows: on a phone the button never covers the bar.\n */\nimport { useLayoutEffect, useState, type RefObject } from 'react'\nimport { CALL_BAR_MEDIA_QUERY, CALL_BAR_STATE_ATTRIBUTE, CALL_BAR_STATE_EVENT, CALL_PATH_SELECTOR } from './constants.js'\n\nconst FIELD_SELECTOR = [\n 'input:not([type=\"button\"], [type=\"submit\"], [type=\"reset\"], [type=\"checkbox\"], [type=\"radio\"], [type=\"hidden\"])',\n 'textarea',\n 'select',\n '[contenteditable=\"\"]',\n '[contenteditable=\"true\"]',\n].join(', ')\n\nfunction isField(target: EventTarget | null): boolean {\n return target instanceof Element && target.matches(FIELD_SELECTOR)\n}\n\n/** The page footer: the shared footer, or a `<footer>` that isn't inside an article, aside, main, nav or section. */\nfunction isPageFooter(element: Element): boolean {\n return element.matches('[data-bl=\"footer\"]') || !element.parentElement?.closest('article, aside, main, nav, section')\n}\n\nfunction pageFooters(): Element[] {\n return Array.from(document.querySelectorAll('footer, [data-bl=\"footer\"]')).filter(isPageFooter)\n}\n\n/**\n * Focus inside the footer or a call path hides the bar at once, before the observer reports (fast Tab stops would\n * otherwise sit under the bar, K5).\n */\nfunction inHideZone(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false\n if (target.closest(CALL_PATH_SELECTOR)) return true\n const footer = target.closest('footer, [data-bl=\"footer\"]')\n return footer !== null && isPageFooter(footer)\n}\n\n/** True when any part of the element's box is inside the viewport. */\nfunction inViewport(element: Element): boolean {\n const box = element.getBoundingClientRect()\n return box.bottom > 0 && box.right > 0 && box.top < window.innerHeight && box.left < window.innerWidth\n}\n\n/**\n * True while the bar should show. `resetKey` (the route) re-collects the call paths and the footer. The first state\n * is seeded from their boxes before paint, so the bar never flashes on load or on a client navigation while the\n * observer's first report is pending.\n */\nexport function useStickyCallBar(rootRef: RefObject<HTMLElement | null>, resetKey: unknown): boolean {\n const [shown, setShown] = useState(false)\n\n useLayoutEffect(() => {\n const root = rootRef.current\n if (!root) return\n const html = document.documentElement\n const narrow = typeof window.matchMedia === 'function' ? window.matchMedia(CALL_BAR_MEDIA_QUERY) : null\n const onScreen = new Set<Element>()\n let fieldFocused = isField(document.activeElement)\n let focusHidden = inHideZone(document.activeElement)\n\n let published: boolean | undefined\n const update = () => {\n const next = (narrow?.matches ?? false) && onScreen.size === 0 && !fieldFocused && !focusHidden && !document.querySelector('dialog[open]')\n setShown(next)\n html.style.setProperty('--bl-callbar-height', next ? `${Math.round(root.getBoundingClientRect().height)}px` : '0px')\n if (next === published) return\n published = next\n html.setAttribute(CALL_BAR_STATE_ATTRIBUTE, next ? 'shown' : 'hidden')\n document.dispatchEvent(new CustomEvent(CALL_BAR_STATE_EVENT, { detail: { shown: next } }))\n }\n\n let intersections: IntersectionObserver | undefined\n if (typeof IntersectionObserver === 'function') {\n intersections = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) onScreen.add(entry.target)\n else onScreen.delete(entry.target)\n }\n update()\n })\n }\n for (const target of [...document.querySelectorAll(CALL_PATH_SELECTOR), ...pageFooters()]) {\n if (inViewport(target)) onScreen.add(target)\n intersections?.observe(target)\n }\n const dialogs = new MutationObserver(update)\n dialogs.observe(document.body, { subtree: true, attributes: true, attributeFilter: ['open'] })\n const onFocusIn = (event: FocusEvent) => {\n fieldFocused = isField(event.target)\n focusHidden = inHideZone(event.target)\n update()\n }\n const onFocusOut = (event: FocusEvent) => {\n fieldFocused = isField(event.relatedTarget)\n focusHidden = inHideZone(event.relatedTarget)\n update()\n }\n document.addEventListener('focusin', onFocusIn)\n document.addEventListener('focusout', onFocusOut)\n narrow?.addEventListener('change', update)\n update()\n\n return () => {\n intersections?.disconnect()\n dialogs.disconnect()\n document.removeEventListener('focusin', onFocusIn)\n document.removeEventListener('focusout', onFocusOut)\n narrow?.removeEventListener('change', update)\n html.style.removeProperty('--bl-callbar-height')\n html.removeAttribute(CALL_BAR_STATE_ATTRIBUTE)\n document.dispatchEvent(new CustomEvent(CALL_BAR_STATE_EVENT, { detail: { shown: false } }))\n }\n }, [rootRef, resetKey])\n\n return shown\n}\n"]}
|
package/dist/next-config.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type BrandlabRedirectTable } from '@brandlab-digital/site-core/redirects';
|
|
2
2
|
import { type BrandlabDeploymentEnvironment, type BrandlabEnvironment, type BrandlabSite } from '@brandlab-digital/site-core/site';
|
|
3
3
|
import { type BrandlabContentSecurityPolicyExtensions, type BrandlabHeader, type BrandlabHeaderRule, type BrandlabHstsExpansion } from './headers.js';
|
|
4
|
+
import { type SiteIconSet } from './ui-kit/icon-glyphs.js';
|
|
4
5
|
/** Next redirect records: 308 stays `permanent: true`, 301 stays `statusCode: 301`. */
|
|
5
6
|
export type BrandlabNextRedirect = {
|
|
6
7
|
source: string;
|
|
@@ -56,6 +57,8 @@ export interface BrandlabNextConfig {
|
|
|
56
57
|
skipTrailingSlashRedirect: true;
|
|
57
58
|
env: {
|
|
58
59
|
NEXT_PUBLIC_VERCEL_ENV: BrandlabDeploymentEnvironment;
|
|
60
|
+
/** The icon sprite's path (3.14.0), set when `icons` is declared. */
|
|
61
|
+
NEXT_PUBLIC_BL_ICON_SPRITE?: string;
|
|
59
62
|
};
|
|
60
63
|
outputFileTracingIncludes: Record<string, string[]>;
|
|
61
64
|
images: BrandlabNextImagesConfig;
|
|
@@ -91,11 +94,24 @@ export interface BrandlabNextConfigOptions {
|
|
|
91
94
|
* no route under `src/app` or `app` may answer below it.
|
|
92
95
|
*/
|
|
93
96
|
staticAssetCache?: readonly string[];
|
|
97
|
+
/**
|
|
98
|
+
* The ui-kit icon sprite (3.14.0). When declared, loading the config writes `public/_bl/icons.<hash>.svg` (every
|
|
99
|
+
* library icon plus `site`, the set from `defineSiteIcons`), removes older sprites there and sets
|
|
100
|
+
* `NEXT_PUBLIC_BL_ICON_SPRITE`, so every `Icon` renders a `<use>` into the sprite instead of an inline glyph.
|
|
101
|
+
* Production builds serve the file with `public, max-age=31536000, immutable`. Add `public/_bl/` to `.gitignore`.
|
|
102
|
+
*/
|
|
103
|
+
icons?: BrandlabIconsOptions;
|
|
94
104
|
/** Defaults to process.env; tests supply a fixed environment. */
|
|
95
105
|
environment?: BrandlabEnvironment;
|
|
96
|
-
/** The project root for the staticAssetCache checks; defaults to process.cwd(). */
|
|
106
|
+
/** The project root for the staticAssetCache checks and the icon sprite; defaults to process.cwd(). */
|
|
97
107
|
projectRoot?: string;
|
|
98
108
|
}
|
|
109
|
+
export interface BrandlabIconsOptions {
|
|
110
|
+
/** The site's own icons, from `defineSiteIcons` (`src/site/icons.ts`). */
|
|
111
|
+
site?: SiteIconSet;
|
|
112
|
+
}
|
|
113
|
+
/** The sprite's cache rule: content-hashed names only, so a stale name never reaches a page. */
|
|
114
|
+
export declare const BRANDLAB_ICON_SPRITE_SOURCE = "/_bl/:file(icons\\.[0-9a-f]+\\.svg)";
|
|
99
115
|
/** Sharp's Linux binaries must travel with the contact route's image sanitiser. */
|
|
100
116
|
export declare const BRANDLAB_CONTACT_OUTPUT_FILE_TRACING: Readonly<{
|
|
101
117
|
'/api/contact': readonly string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next-config.d.ts","sourceRoot":"","sources":["../src/next-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,qBAAqB,EAAE,MAAM,uCAAuC,CAAA;AACnG,OAAO,EAGL,KAAK,6BAA6B,EAClC,KAAK,mBAAmB,EACxB,KAAK,YAAY,EAClB,MAAM,kCAAkC,CAAA;AACzC,OAAO,
|
|
1
|
+
{"version":3,"file":"next-config.d.ts","sourceRoot":"","sources":["../src/next-config.ts"],"names":[],"mappings":"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"}
|
package/dist/next-config.js
CHANGED
|
@@ -2,7 +2,11 @@ import { readdirSync, statSync } from 'node:fs';
|
|
|
2
2
|
import { join, posix } from 'node:path';
|
|
3
3
|
import { defineRedirects } from '@brandlab-digital/site-core/redirects';
|
|
4
4
|
import { assertBrandlabAliasHosts, resolveBrandlabDeployment, } from '@brandlab-digital/site-core/site';
|
|
5
|
-
import { buildBrandlabHeaderRules, parseBrandlabStaticAssetCache, } from './headers.js';
|
|
5
|
+
import { BRANDLAB_STATIC_ASSET_CACHE_CONTROL, buildBrandlabHeaderRules, parseBrandlabStaticAssetCache, } from './headers.js';
|
|
6
|
+
import { defineSiteIcons, ICON_SPRITE_ENV } from './ui-kit/icon-glyphs.js';
|
|
7
|
+
import { writeIconSprite } from './ui-kit/icon-sprite.js';
|
|
8
|
+
/** The sprite's cache rule: content-hashed names only, so a stale name never reaches a page. */
|
|
9
|
+
export const BRANDLAB_ICON_SPRITE_SOURCE = '/_bl/:file(icons\\.[0-9a-f]+\\.svg)';
|
|
6
10
|
/** Sharp's Linux binaries must travel with the contact route's image sanitiser. */
|
|
7
11
|
export const BRANDLAB_CONTACT_OUTPUT_FILE_TRACING = Object.freeze({
|
|
8
12
|
'/api/contact': Object.freeze([
|
|
@@ -23,6 +27,7 @@ const OPTION_KEYS = new Set([
|
|
|
23
27
|
'extraHeaders',
|
|
24
28
|
'staticAssetCache',
|
|
25
29
|
'mdx',
|
|
30
|
+
'icons',
|
|
26
31
|
'environment',
|
|
27
32
|
'projectRoot',
|
|
28
33
|
]);
|
|
@@ -241,6 +246,16 @@ function assertStaticAssetFolders(folders, root) {
|
|
|
241
246
|
}
|
|
242
247
|
}
|
|
243
248
|
}
|
|
249
|
+
/** A site icon set, checked again here: the config may be handed an object that never went through defineSiteIcons. */
|
|
250
|
+
function siteIconSet(site) {
|
|
251
|
+
if (site === undefined)
|
|
252
|
+
return undefined;
|
|
253
|
+
const set = site;
|
|
254
|
+
if (!set || typeof set !== 'object' || !Array.isArray(set.names) || !set.glyphs || typeof set.glyphs !== 'object') {
|
|
255
|
+
throw new Error('icons.site must come from defineSiteIcons.');
|
|
256
|
+
}
|
|
257
|
+
return defineSiteIcons(Object.fromEntries(set.names.map((name) => [name, set.glyphs[name]])));
|
|
258
|
+
}
|
|
244
259
|
/**
|
|
245
260
|
* Converts the redirect table to Next redirects: each entry keeps its 301 or
|
|
246
261
|
* 308 status (Next forwards the query string), gains a single-hop
|
|
@@ -315,6 +330,22 @@ export function defineBrandlabNextConfig(options) {
|
|
|
315
330
|
...(options.extraHeaders ? { extraHeaders: options.extraHeaders } : {}),
|
|
316
331
|
...(options.staticAssetCache ? { staticAssetCache: options.staticAssetCache } : {}),
|
|
317
332
|
});
|
|
333
|
+
if (options.icons !== undefined) {
|
|
334
|
+
if (!options.icons || typeof options.icons !== 'object' || Array.isArray(options.icons)) {
|
|
335
|
+
throw new Error('icons must be { site?: defineSiteIcons(...) } ({} for the library icons only).');
|
|
336
|
+
}
|
|
337
|
+
assertKnownKeys(options.icons, new Set(['site']), 'icons');
|
|
338
|
+
}
|
|
339
|
+
const sprite = options.icons === undefined ? undefined : writeIconSprite(options.projectRoot ?? process.cwd(), siteIconSet(options.icons.site));
|
|
340
|
+
// The same production-only rule as staticAssetCache: Previews keep private, no-store.
|
|
341
|
+
if (sprite && deployment.productionBuild && deployment.environment !== 'preview') {
|
|
342
|
+
// After the static folders, before the non-canonical host rule.
|
|
343
|
+
const at = headerRules.findIndex((rule) => rule.missing !== undefined);
|
|
344
|
+
headerRules.splice(at === -1 ? headerRules.length : at, 0, {
|
|
345
|
+
source: BRANDLAB_ICON_SPRITE_SOURCE,
|
|
346
|
+
headers: [{ key: 'Cache-Control', value: BRANDLAB_STATIC_ASSET_CACHE_CONTROL }],
|
|
347
|
+
});
|
|
348
|
+
}
|
|
318
349
|
const redirects = buildBrandlabRedirects({
|
|
319
350
|
site: options.site,
|
|
320
351
|
redirects: options.redirects,
|
|
@@ -323,7 +354,10 @@ export function defineBrandlabNextConfig(options) {
|
|
|
323
354
|
const config = {
|
|
324
355
|
poweredByHeader: false,
|
|
325
356
|
skipTrailingSlashRedirect: true,
|
|
326
|
-
env: {
|
|
357
|
+
env: {
|
|
358
|
+
NEXT_PUBLIC_VERCEL_ENV: deployment.environment,
|
|
359
|
+
...(sprite ? { [ICON_SPRITE_ENV]: sprite.href } : {}),
|
|
360
|
+
},
|
|
327
361
|
outputFileTracingIncludes: Object.fromEntries(Object.entries(BRANDLAB_CONTACT_OUTPUT_FILE_TRACING).map(([route, globs]) => [route, [...globs]])),
|
|
328
362
|
// A Vercel build (preview or production) is checked like any production build, whatever NODE_ENV says.
|
|
329
363
|
images: buildImagesConfig(options.images, deployment.productionBuild || deployment.environment !== 'development'),
|