@sankhyatronics/sankhya-cms 0.2.0 → 0.3.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/custom-elements.json +10968 -3080
- package/dist/attention-panel/index.js +2 -2
- package/dist/bar-chart/index.js +2 -2
- package/dist/bento-grid/index.d.ts +25 -0
- package/dist/bento-grid/index.js +4 -4
- package/dist/booking/index.d.ts +309 -0
- package/dist/booking/index.js +20 -0
- package/dist/booking/index.js.map +1 -0
- package/dist/card/index.js +2 -2
- package/dist/carousel/index.d.ts +20 -6
- package/dist/carousel/index.js +3 -3
- package/dist/chip/index.js +2 -2
- package/dist/{chunk-OLA35A2O.js → chunk-2GTR37PU.js} +2 -2
- package/dist/{chunk-7NIWQWTA.js → chunk-2HMOX253.js} +128 -25
- package/dist/chunk-2HMOX253.js.map +1 -0
- package/dist/{chunk-DH53HZPB.js → chunk-37KFBKPD.js} +32 -4
- package/dist/chunk-37KFBKPD.js.map +1 -0
- package/dist/{chunk-EE6PAN3B.js → chunk-4XPKKYVZ.js} +23 -15
- package/dist/chunk-4XPKKYVZ.js.map +1 -0
- package/dist/chunk-535QAZ6A.js +236 -0
- package/dist/chunk-535QAZ6A.js.map +1 -0
- package/dist/{chunk-AKJQ345O.js → chunk-5AWGZQWU.js} +2 -2
- package/dist/chunk-6OZKZG5J.js +300 -0
- package/dist/chunk-6OZKZG5J.js.map +1 -0
- package/dist/chunk-AYNT7G3J.js +805 -0
- package/dist/chunk-AYNT7G3J.js.map +1 -0
- package/dist/chunk-BEWX3E6P.js +32 -0
- package/dist/chunk-BEWX3E6P.js.map +1 -0
- package/dist/chunk-CZUGPKXV.js +275 -0
- package/dist/chunk-CZUGPKXV.js.map +1 -0
- package/dist/{chunk-2DXHPEB5.js → chunk-DJFWZOZM.js} +107 -11
- package/dist/chunk-DJFWZOZM.js.map +1 -0
- package/dist/chunk-DPANTC3O.js +1714 -0
- package/dist/chunk-DPANTC3O.js.map +1 -0
- package/dist/chunk-EKZFFY45.js +426 -0
- package/dist/chunk-EKZFFY45.js.map +1 -0
- package/dist/chunk-ER7MCPDV.js +7 -0
- package/dist/chunk-ER7MCPDV.js.map +1 -0
- package/dist/chunk-EW2RK5Y6.js +383 -0
- package/dist/chunk-EW2RK5Y6.js.map +1 -0
- package/dist/{chunk-BLQV2YMO.js → chunk-EZOKT7NJ.js} +2 -2
- package/dist/chunk-FF2LWEF3.js +386 -0
- package/dist/chunk-FF2LWEF3.js.map +1 -0
- package/dist/chunk-FOUK3NHR.js +7 -0
- package/dist/chunk-FOUK3NHR.js.map +1 -0
- package/dist/{chunk-O4FCRKDX.js → chunk-GB4NGCZI.js} +6 -3
- package/dist/chunk-GB4NGCZI.js.map +1 -0
- package/dist/{chunk-5F5PTVWR.js → chunk-GOFSMD6W.js} +9 -10
- package/dist/chunk-GOFSMD6W.js.map +1 -0
- package/dist/{chunk-6D6ROU7X.js → chunk-IGMVF3KK.js} +2 -2
- package/dist/{chunk-INMSEYT6.js → chunk-IVPVYVWG.js} +2 -2
- package/dist/{chunk-ZRC5BVDX.js → chunk-JURCF4OD.js} +174 -12
- package/dist/chunk-JURCF4OD.js.map +1 -0
- package/dist/{chunk-FZ36KL73.js → chunk-KZ7PMFWS.js} +11 -1
- package/dist/chunk-KZ7PMFWS.js.map +1 -0
- package/dist/{chunk-EMCQBT3S.js → chunk-LO6TZBGD.js} +129 -35
- package/dist/chunk-LO6TZBGD.js.map +1 -0
- package/dist/chunk-MFRGAT6D.js +860 -0
- package/dist/chunk-MFRGAT6D.js.map +1 -0
- package/dist/chunk-MIBVQXRU.js +1098 -0
- package/dist/chunk-MIBVQXRU.js.map +1 -0
- package/dist/{chunk-35TX3AOA.js → chunk-NABSRUXG.js} +2 -2
- package/dist/{chunk-2FJDNJ7R.js → chunk-NXKDZWS7.js} +44 -6
- package/dist/chunk-NXKDZWS7.js.map +1 -0
- package/dist/{chunk-KMU6ABQF.js → chunk-NXUS2CH7.js} +14 -1
- package/dist/chunk-NXUS2CH7.js.map +1 -0
- package/dist/{chunk-GJO3RGOB.js → chunk-OCPGOLVF.js} +56 -11
- package/dist/chunk-OCPGOLVF.js.map +1 -0
- package/dist/chunk-OEOE6IFY.js +628 -0
- package/dist/chunk-OEOE6IFY.js.map +1 -0
- package/dist/{chunk-OELI3IWC.js → chunk-PV6OCXXN.js} +2 -2
- package/dist/{chunk-5C6C6K5U.js → chunk-Q66CJO3P.js} +200 -12
- package/dist/chunk-Q66CJO3P.js.map +1 -0
- package/dist/{chunk-MG2VTZA4.js → chunk-QS3ANOWN.js} +2 -2
- package/dist/{chunk-3OEEOBHB.js → chunk-QSJ543V2.js} +2 -2
- package/dist/{chunk-FML6AG2V.js → chunk-RANC3OI6.js} +2 -2
- package/dist/chunk-T4BIN37Q.js +32 -0
- package/dist/chunk-T4BIN37Q.js.map +1 -0
- package/dist/{chunk-YVTFQHVY.js → chunk-TEAZZPDQ.js} +2 -2
- package/dist/chunk-TJK55CJ2.js +49 -0
- package/dist/chunk-TJK55CJ2.js.map +1 -0
- package/dist/chunk-UKIVLF7S.js +32 -0
- package/dist/chunk-UKIVLF7S.js.map +1 -0
- package/dist/chunk-V4R7PYP5.js +905 -0
- package/dist/chunk-V4R7PYP5.js.map +1 -0
- package/dist/{chunk-AG2COC64.js → chunk-VVYQ2DDS.js} +2 -2
- package/dist/chunk-WAMOAAMN.js +16 -0
- package/dist/chunk-WAMOAAMN.js.map +1 -0
- package/dist/chunk-WAYHHL4J.js +1234 -0
- package/dist/chunk-WAYHHL4J.js.map +1 -0
- package/dist/{chunk-PHEGSCOM.js → chunk-WH6HE6SY.js} +10 -4
- package/dist/chunk-WH6HE6SY.js.map +1 -0
- package/dist/{chunk-5MY6OWMK.js → chunk-XP6DI3VY.js} +59 -4
- package/dist/chunk-XP6DI3VY.js.map +1 -0
- package/dist/{chunk-QLA4BH2B.js → chunk-XS6LNTSH.js} +73 -11
- package/dist/chunk-XS6LNTSH.js.map +1 -0
- package/dist/{chunk-WT4OATGP.js → chunk-ZKFCDVB4.js} +16 -7
- package/dist/chunk-ZKFCDVB4.js.map +1 -0
- package/dist/cms-theme.css +22 -0
- package/dist/contact-form/index.d.ts +220 -0
- package/dist/contact-form/index.js +16 -0
- package/dist/contact-form/index.js.map +1 -0
- package/dist/content-block/index.d.ts +39 -3
- package/dist/content-block/index.js +8 -5
- package/dist/cookie-consent/index.d.ts +60 -12
- package/dist/cookie-consent/index.js +3 -2
- package/dist/date-tile/index.js +2 -2
- package/dist/dropdown/index.d.ts +22 -1
- package/dist/dropdown/index.js +4 -2
- package/dist/feature-split/index.d.ts +6 -1
- package/dist/feature-split/index.js +5 -5
- package/dist/features-section/index.d.ts +49 -5
- package/dist/features-section/index.js +6 -6
- package/dist/flow/index.js +2 -2
- package/dist/footer/index.d.ts +13 -0
- package/dist/footer/index.js +2 -2
- package/dist/header/index.d.ts +127 -1
- package/dist/header/index.js +3 -2
- package/dist/hero/index.d.ts +75 -6
- package/dist/hero/index.js +6 -3
- package/dist/icon-button/index.d.ts +5 -3
- package/dist/icon-button/index.js +2 -2
- package/dist/index.d.ts +9 -3
- package/dist/index.js +118 -57
- package/dist/items-accordion/index.d.ts +5 -1
- package/dist/items-accordion/index.js +4 -4
- package/dist/kpi-card/index.js +2 -2
- package/dist/markdown/index.d.ts +18 -0
- package/dist/markdown/index.js +3 -3
- package/dist/mega-menu/index.d.ts +95 -7
- package/dist/mega-menu/index.js +7 -2
- package/dist/menu-grid/index.js +2 -2
- package/dist/menu-grid-item/index.js +2 -2
- package/dist/menu-item/index.d.ts +6 -0
- package/dist/menu-item/index.js +3 -2
- package/dist/plan-cards/index.d.ts +69 -0
- package/dist/plan-cards/index.js +13 -0
- package/dist/plan-cards/index.js.map +1 -0
- package/dist/product-nav/index.d.ts +127 -0
- package/dist/product-nav/index.js +11 -0
- package/dist/product-nav/index.js.map +1 -0
- package/dist/promo-banner/index.d.ts +34 -7
- package/dist/promo-banner/index.js +6 -3
- package/dist/quote-dialog/index.d.ts +174 -0
- package/dist/quote-dialog/index.js +22 -0
- package/dist/quote-dialog/index.js.map +1 -0
- package/dist/schema/index.d.ts +8 -0
- package/dist/schema/index.js +6 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/screenshot-tour/index.d.ts +54 -0
- package/dist/screenshot-tour/index.js +9 -0
- package/dist/screenshot-tour/index.js.map +1 -0
- package/dist/section/index.js +3 -3
- package/dist/segmented-control/index.js +2 -2
- package/dist/select/index.js +2 -2
- package/dist/shared/styles.d.ts +7 -1
- package/dist/shared/styles.js +3 -1
- package/dist/stats/index.js +2 -2
- package/dist/steps/index.d.ts +12 -0
- package/dist/steps/index.js +4 -3
- package/dist/tag-strip/index.d.ts +7 -0
- package/dist/tag-strip/index.js +2 -2
- package/dist/testimonials/index.d.ts +9 -1
- package/dist/testimonials/index.js +3 -3
- package/dist/tooltip/index.js +2 -2
- package/package.json +26 -2
- package/dist/chunk-2DXHPEB5.js.map +0 -1
- package/dist/chunk-2FJDNJ7R.js.map +0 -1
- package/dist/chunk-4AFOKKSU.js +0 -158
- package/dist/chunk-4AFOKKSU.js.map +0 -1
- package/dist/chunk-5C6C6K5U.js.map +0 -1
- package/dist/chunk-5F5PTVWR.js.map +0 -1
- package/dist/chunk-5MY6OWMK.js.map +0 -1
- package/dist/chunk-7NIWQWTA.js.map +0 -1
- package/dist/chunk-DH53HZPB.js.map +0 -1
- package/dist/chunk-EE6PAN3B.js.map +0 -1
- package/dist/chunk-EMCQBT3S.js.map +0 -1
- package/dist/chunk-FZ36KL73.js.map +0 -1
- package/dist/chunk-GJO3RGOB.js.map +0 -1
- package/dist/chunk-HMUZON6I.js +0 -202
- package/dist/chunk-HMUZON6I.js.map +0 -1
- package/dist/chunk-J6EO4R73.js +0 -633
- package/dist/chunk-J6EO4R73.js.map +0 -1
- package/dist/chunk-KMU6ABQF.js.map +0 -1
- package/dist/chunk-MSSH3HFK.js +0 -205
- package/dist/chunk-MSSH3HFK.js.map +0 -1
- package/dist/chunk-O4FCRKDX.js.map +0 -1
- package/dist/chunk-PHEGSCOM.js.map +0 -1
- package/dist/chunk-QLA4BH2B.js.map +0 -1
- package/dist/chunk-SQ3OZO32.js +0 -486
- package/dist/chunk-SQ3OZO32.js.map +0 -1
- package/dist/chunk-UAIUBPDN.js +0 -11
- package/dist/chunk-UAIUBPDN.js.map +0 -1
- package/dist/chunk-WT4OATGP.js.map +0 -1
- package/dist/chunk-XUGEXERO.js +0 -708
- package/dist/chunk-XUGEXERO.js.map +0 -1
- package/dist/chunk-ZRC5BVDX.js.map +0 -1
- /package/dist/{chunk-OLA35A2O.js.map → chunk-2GTR37PU.js.map} +0 -0
- /package/dist/{chunk-AKJQ345O.js.map → chunk-5AWGZQWU.js.map} +0 -0
- /package/dist/{chunk-BLQV2YMO.js.map → chunk-EZOKT7NJ.js.map} +0 -0
- /package/dist/{chunk-6D6ROU7X.js.map → chunk-IGMVF3KK.js.map} +0 -0
- /package/dist/{chunk-INMSEYT6.js.map → chunk-IVPVYVWG.js.map} +0 -0
- /package/dist/{chunk-35TX3AOA.js.map → chunk-NABSRUXG.js.map} +0 -0
- /package/dist/{chunk-OELI3IWC.js.map → chunk-PV6OCXXN.js.map} +0 -0
- /package/dist/{chunk-MG2VTZA4.js.map → chunk-QS3ANOWN.js.map} +0 -0
- /package/dist/{chunk-3OEEOBHB.js.map → chunk-QSJ543V2.js.map} +0 -0
- /package/dist/{chunk-FML6AG2V.js.map → chunk-RANC3OI6.js.map} +0 -0
- /package/dist/{chunk-YVTFQHVY.js.map → chunk-TEAZZPDQ.js.map} +0 -0
- /package/dist/{chunk-AG2COC64.js.map → chunk-VVYQ2DDS.js.map} +0 -0
|
@@ -0,0 +1,1234 @@
|
|
|
1
|
+
import {
|
|
2
|
+
setModalOpen
|
|
3
|
+
} from "./chunk-WAMOAAMN.js";
|
|
4
|
+
import {
|
|
5
|
+
chevronIcon,
|
|
6
|
+
closeIcon,
|
|
7
|
+
menuIcon
|
|
8
|
+
} from "./chunk-WTD2YIAA.js";
|
|
9
|
+
import {
|
|
10
|
+
BREAKPOINT_TABLET,
|
|
11
|
+
buttonResetStyles,
|
|
12
|
+
controlBaseStyles,
|
|
13
|
+
dropdownPanelStyles,
|
|
14
|
+
rootFontFamilyStyles
|
|
15
|
+
} from "./chunk-NXUS2CH7.js";
|
|
16
|
+
import {
|
|
17
|
+
__decorateClass
|
|
18
|
+
} from "./chunk-EUXUH3YW.js";
|
|
19
|
+
|
|
20
|
+
// src/header/Header.ts
|
|
21
|
+
import { LitElement, css, html, nothing } from "lit";
|
|
22
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
23
|
+
var NAV_SELECTOR = "st-menu-item, st-mega-menu-column, st-mega-menu-feature, st-dropdown";
|
|
24
|
+
var CONTEXT_BAR_HEIGHT = 64;
|
|
25
|
+
var FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
26
|
+
function isRendered(el) {
|
|
27
|
+
if (el.getClientRects().length === 0) return false;
|
|
28
|
+
const check = el.checkVisibility;
|
|
29
|
+
return check ? check.call(el, { visibilityProperty: true }) : true;
|
|
30
|
+
}
|
|
31
|
+
function renderedFocusables(roots) {
|
|
32
|
+
const found = /* @__PURE__ */ new Set();
|
|
33
|
+
const visit = (el) => {
|
|
34
|
+
if (el instanceof HTMLSlotElement) {
|
|
35
|
+
for (const assigned of el.assignedElements({ flatten: true })) visit(assigned);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (el.matches(FOCUSABLE_SELECTOR) && isRendered(el)) found.add(el);
|
|
39
|
+
const children = el.shadowRoot ? el.shadowRoot.children : el.children;
|
|
40
|
+
for (const child of Array.from(children)) visit(child);
|
|
41
|
+
};
|
|
42
|
+
roots.forEach(visit);
|
|
43
|
+
return [...found];
|
|
44
|
+
}
|
|
45
|
+
function deepActiveElement() {
|
|
46
|
+
let el = document.activeElement;
|
|
47
|
+
while (el?.shadowRoot?.activeElement) el = el.shadowRoot.activeElement;
|
|
48
|
+
return el;
|
|
49
|
+
}
|
|
50
|
+
var CONTEXT_TRIGGER_TIMEOUT_MS = 5e3;
|
|
51
|
+
var DRAWER_OPEN_ATTRIBUTE = "data-st-drawer-open";
|
|
52
|
+
var Header = class extends LitElement {
|
|
53
|
+
constructor() {
|
|
54
|
+
super(...arguments);
|
|
55
|
+
this.title = "";
|
|
56
|
+
this.brandImageUrl = "";
|
|
57
|
+
this.currentPath = "";
|
|
58
|
+
this.altText = "Logo";
|
|
59
|
+
this.logoHref = "";
|
|
60
|
+
this.logoTarget = "_self";
|
|
61
|
+
this.logoClassName = "";
|
|
62
|
+
this.inverted = false;
|
|
63
|
+
this.sticky = false;
|
|
64
|
+
this.transparent = false;
|
|
65
|
+
this.hideOnScroll = false;
|
|
66
|
+
this.scrollThreshold = 8;
|
|
67
|
+
this.scrolled = false;
|
|
68
|
+
this.scrollHidden = false;
|
|
69
|
+
this.lastScrollY = 0;
|
|
70
|
+
this.scrollFrame = 0;
|
|
71
|
+
/** The `<html>` overflow and padding to restore when the drawer closes; null while the page is not locked. */
|
|
72
|
+
this.drawerScrollLock = null;
|
|
73
|
+
this.onScroll = () => {
|
|
74
|
+
if (this.scrollFrame) return;
|
|
75
|
+
this.scrollFrame = requestAnimationFrame(() => {
|
|
76
|
+
this.scrollFrame = 0;
|
|
77
|
+
const y = Math.max(0, window.scrollY);
|
|
78
|
+
this.scrolled = y > this.scrollThreshold;
|
|
79
|
+
if (this.hideOnScroll) {
|
|
80
|
+
const delta = y - this.lastScrollY;
|
|
81
|
+
if (Math.abs(delta) > 4) this.scrollHidden = delta > 0 && y > this.offsetHeight && !this.mobileOpen;
|
|
82
|
+
} else {
|
|
83
|
+
this.scrollHidden = false;
|
|
84
|
+
}
|
|
85
|
+
this.lastScrollY = y;
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
this.languages = [];
|
|
89
|
+
this.currentLanguageCode = "";
|
|
90
|
+
this.languageMenuLabel = "Language";
|
|
91
|
+
this.mobileOpen = false;
|
|
92
|
+
this.compact = false;
|
|
93
|
+
this.ctaLabel = "";
|
|
94
|
+
this.ctaHref = "";
|
|
95
|
+
this.languageMenuOpen = false;
|
|
96
|
+
this.hasUtility = false;
|
|
97
|
+
this.hasBrandExtra = false;
|
|
98
|
+
this.contextTrigger = "";
|
|
99
|
+
this.contextActive = false;
|
|
100
|
+
this.hasContext = false;
|
|
101
|
+
this.siteMenuRequested = false;
|
|
102
|
+
/** The product nav's site-menu button that asked for the menu; focus returns to it when the menu closes. */
|
|
103
|
+
this.siteMenuTrigger = null;
|
|
104
|
+
/** Whether the trigger element has scrolled up past the bar. */
|
|
105
|
+
this.pastTrigger = false;
|
|
106
|
+
this.triggerTimer = 0;
|
|
107
|
+
/** The dropdowns this header switched to `inline` — only these are switched back, so a caller's
|
|
108
|
+
* own `inline` setting is left alone. */
|
|
109
|
+
this.inlinedDropdowns = /* @__PURE__ */ new Set();
|
|
110
|
+
/** While the hamburger layout is on, the dropdowns in the header open in the flow of the
|
|
111
|
+
* full-screen panel instead of floating over it (the touch-device layout). */
|
|
112
|
+
this.syncInlineDropdowns = () => {
|
|
113
|
+
const collapsed = this.compact || !!this.narrowQuery?.matches;
|
|
114
|
+
const dropdowns = Array.from(this.querySelectorAll("st-dropdown"));
|
|
115
|
+
if (collapsed) {
|
|
116
|
+
for (const dropdown of dropdowns) {
|
|
117
|
+
if (dropdown.inline) continue;
|
|
118
|
+
dropdown.inline = true;
|
|
119
|
+
this.inlinedDropdowns.add(dropdown);
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
for (const dropdown of this.inlinedDropdowns) dropdown.inline = false;
|
|
123
|
+
this.inlinedDropdowns.clear();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
this.onBrandExtraSlotChange = (event) => {
|
|
127
|
+
this.hasBrandExtra = event.target.assignedElements({ flatten: true }).length > 0;
|
|
128
|
+
this.evaluateFit();
|
|
129
|
+
};
|
|
130
|
+
this.onUtilitySlotChange = (event) => {
|
|
131
|
+
this.hasUtility = event.target.assignedElements({ flatten: true }).length > 0;
|
|
132
|
+
this.evaluateFit();
|
|
133
|
+
};
|
|
134
|
+
this.onMenuSlotChange = () => {
|
|
135
|
+
this.evaluateFit();
|
|
136
|
+
this.syncInlineDropdowns();
|
|
137
|
+
this.syncCurrentPath();
|
|
138
|
+
};
|
|
139
|
+
/** Hands the current page path to every nav element under the header, which marks its own link current. */
|
|
140
|
+
this.syncCurrentPath = () => {
|
|
141
|
+
const path = this.currentPath || window.location.pathname;
|
|
142
|
+
for (const el of this.querySelectorAll(NAV_SELECTOR)) {
|
|
143
|
+
el.currentPath = path;
|
|
144
|
+
el.requestUpdate();
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/** Runs when the light DOM changes: a late-rendered dropdown or nav item needs the same state as the rest. */
|
|
148
|
+
this.onLightDomChange = () => {
|
|
149
|
+
this.syncInlineDropdowns();
|
|
150
|
+
this.syncCurrentPath();
|
|
151
|
+
};
|
|
152
|
+
/** The bar width at which the menu last overflowed — the bar only expands again once it is
|
|
153
|
+
* clearly wider than this, so it can't flip back and forth right at the threshold. */
|
|
154
|
+
this.collapsedAtWidth = 0;
|
|
155
|
+
/** Collapses to the hamburger layout when the slotted menu overflows its row, and expands again
|
|
156
|
+
* once there is room. The collapsed layout hides the menu, so the room it needs can't be
|
|
157
|
+
* measured then; the width remembered when it collapsed stands in for it. */
|
|
158
|
+
this.evaluateFit = () => {
|
|
159
|
+
if (this.siteMenuRequested) return;
|
|
160
|
+
const width = this.clientWidth;
|
|
161
|
+
if (this.compact) {
|
|
162
|
+
if (width > this.collapsedAtWidth + 24) {
|
|
163
|
+
this.compact = false;
|
|
164
|
+
this.mobileOpen = false;
|
|
165
|
+
void this.updateComplete.then(this.evaluateFit);
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (this.menuOverflows()) {
|
|
170
|
+
this.collapsedAtWidth = width;
|
|
171
|
+
this.compact = true;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
/** Esc closes the open drawer; Tab is held inside it while it is open. */
|
|
175
|
+
this.onKeyDown = (event) => {
|
|
176
|
+
if (this.mobileOpen) {
|
|
177
|
+
if (event.key === "Escape") this.closeDrawer();
|
|
178
|
+
else if (event.key === "Tab") this.trapDrawerFocus(event);
|
|
179
|
+
}
|
|
180
|
+
if (this.languageMenuOpen && event.key === "Escape") this.languageMenuOpen = false;
|
|
181
|
+
};
|
|
182
|
+
this.closeLanguageMenuOnOutsideClick = (event) => {
|
|
183
|
+
if (this.languageMenuOpen && !event.composedPath().includes(this)) this.languageMenuOpen = false;
|
|
184
|
+
};
|
|
185
|
+
/** A click outside the bar closes a site menu opened from the product nav. */
|
|
186
|
+
this.closeSiteMenuOnOutsideClick = (event) => {
|
|
187
|
+
if (this.siteMenuRequested && !event.composedPath().includes(this)) this.endSiteMenu(true);
|
|
188
|
+
};
|
|
189
|
+
/** The product nav's site-menu button asked for the site menu (context mode only). */
|
|
190
|
+
this.onSiteMenuRequest = (event) => {
|
|
191
|
+
if (!this.stacked) return;
|
|
192
|
+
const trigger = event.detail?.trigger ?? null;
|
|
193
|
+
this.openSiteMenu(trigger);
|
|
194
|
+
};
|
|
195
|
+
this.onTriggerIntersect = (entries) => {
|
|
196
|
+
const entry = entries[entries.length - 1];
|
|
197
|
+
if (!entry) return;
|
|
198
|
+
if (!entry.target.isConnected) {
|
|
199
|
+
this.startContextTrigger();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
this.pastTrigger = !entry.isIntersecting && entry.boundingClientRect.bottom <= CONTEXT_BAR_HEIGHT;
|
|
203
|
+
this.updateContextActive();
|
|
204
|
+
};
|
|
205
|
+
this.onContextSlotChange = (event) => {
|
|
206
|
+
this.hasContext = event.target.assignedElements({ flatten: true }).length > 0;
|
|
207
|
+
this.syncSiteMenuOpen();
|
|
208
|
+
this.updateContextActive();
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/** Re-watches the trigger when `context-trigger` changes (before render, so state changes don't re-render twice). */
|
|
212
|
+
willUpdate(changed) {
|
|
213
|
+
if (changed.has("contextTrigger")) this.startContextTrigger();
|
|
214
|
+
}
|
|
215
|
+
updated(changed) {
|
|
216
|
+
this.toggleAttribute("site-menu-request", this.siteMenuRequested);
|
|
217
|
+
this.syncContextSlot();
|
|
218
|
+
this.syncSiteMenuOpen();
|
|
219
|
+
if (changed.has("currentPath")) this.syncCurrentPath();
|
|
220
|
+
if (changed.has("compact")) this.syncInlineDropdowns();
|
|
221
|
+
if (changed.has("mobileOpen")) this.syncDrawerOpen();
|
|
222
|
+
if (changed.has("mobileOpen") && !this.mobileOpen) {
|
|
223
|
+
for (const dropdown of this.inlinedDropdowns) dropdown.open = false;
|
|
224
|
+
this.endSiteMenu(true);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Keeps the page in step with the drawer: `<html data-st-drawer-open>` (which the cookie banner reads) and
|
|
229
|
+
* a scroll lock. The lock gives back the scrollbar's width, so the page does not shift sideways. Opening
|
|
230
|
+
* moves focus to the first item in the drawer; a site-menu request focuses its close button instead.
|
|
231
|
+
*/
|
|
232
|
+
syncDrawerOpen() {
|
|
233
|
+
const root = document.documentElement;
|
|
234
|
+
root.toggleAttribute(DRAWER_OPEN_ATTRIBUTE, this.mobileOpen);
|
|
235
|
+
if (!this.mobileOpen) {
|
|
236
|
+
this.unlockScroll();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (!this.drawerScrollLock) {
|
|
240
|
+
const scrollbarWidth = window.innerWidth - root.clientWidth;
|
|
241
|
+
this.drawerScrollLock = { overflow: root.style.overflow, paddingRight: root.style.paddingRight };
|
|
242
|
+
root.style.overflow = "hidden";
|
|
243
|
+
if (scrollbarWidth > 0) root.style.paddingRight = `${scrollbarWidth}px`;
|
|
244
|
+
}
|
|
245
|
+
if (!this.siteMenuRequested) void this.updateComplete.then(() => this.drawerFocusables()[0]?.focus({ preventScroll: true }));
|
|
246
|
+
}
|
|
247
|
+
unlockScroll() {
|
|
248
|
+
if (!this.drawerScrollLock) return;
|
|
249
|
+
const root = document.documentElement;
|
|
250
|
+
root.style.overflow = this.drawerScrollLock.overflow;
|
|
251
|
+
root.style.paddingRight = this.drawerScrollLock.paddingRight;
|
|
252
|
+
this.drawerScrollLock = null;
|
|
253
|
+
}
|
|
254
|
+
/** What a keyboard user can reach in the open drawer, in order: its own items, then the toggle that closes it. */
|
|
255
|
+
drawerFocusables() {
|
|
256
|
+
const panel = this.renderRoot.querySelector(".mobile-panel");
|
|
257
|
+
const toggle = this.renderRoot.querySelector(".toggle");
|
|
258
|
+
return renderedFocusables([panel, toggle].filter((el) => el !== null));
|
|
259
|
+
}
|
|
260
|
+
/** Keeps Tab and Shift+Tab inside the open drawer, wrapping at either end (and pulling in focus that has left it). */
|
|
261
|
+
trapDrawerFocus(event) {
|
|
262
|
+
const items = this.drawerFocusables();
|
|
263
|
+
if (items.length === 0) return;
|
|
264
|
+
const first = items[0];
|
|
265
|
+
const last = items[items.length - 1];
|
|
266
|
+
const active = deepActiveElement();
|
|
267
|
+
const inside = active !== null && items.includes(active);
|
|
268
|
+
if (event.shiftKey) {
|
|
269
|
+
if (!inside || active === first) {
|
|
270
|
+
event.preventDefault();
|
|
271
|
+
last.focus({ preventScroll: true });
|
|
272
|
+
}
|
|
273
|
+
} else if (!inside || active === last) {
|
|
274
|
+
event.preventDefault();
|
|
275
|
+
first.focus({ preventScroll: true });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Closes the drawer and returns focus to its toggle. A site-menu request returns focus to the product
|
|
280
|
+
* nav's button instead (see endSiteMenu).
|
|
281
|
+
*/
|
|
282
|
+
closeDrawer() {
|
|
283
|
+
if (!this.mobileOpen) return;
|
|
284
|
+
this.mobileOpen = false;
|
|
285
|
+
if (!this.siteMenuRequested) this.renderRoot.querySelector(".toggle")?.focus({ preventScroll: true });
|
|
286
|
+
}
|
|
287
|
+
/** Whether the visible menu items reach past the right edge of the menu row. Measured from the
|
|
288
|
+
* items' own boxes (the slotted elements and their direct children), not the row's scrollWidth:
|
|
289
|
+
* a closed dropdown's absolutely positioned panel also counts towards scrollWidth, which would
|
|
290
|
+
* report overflow at any width. */
|
|
291
|
+
menuOverflows() {
|
|
292
|
+
const row = this.renderRoot.querySelector(".menu-row");
|
|
293
|
+
const slot = this.renderRoot.querySelector('slot[name="menu"]');
|
|
294
|
+
if (!row || !slot) return false;
|
|
295
|
+
const limit = row.getBoundingClientRect().right + 1;
|
|
296
|
+
let right = 0;
|
|
297
|
+
for (const el of slot.assignedElements({ flatten: true })) {
|
|
298
|
+
for (const box of [el, ...Array.from(el.children)]) {
|
|
299
|
+
const rect = box.getBoundingClientRect();
|
|
300
|
+
if (rect.width > 0) right = Math.max(right, rect.right);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return right > limit;
|
|
304
|
+
}
|
|
305
|
+
firstUpdated() {
|
|
306
|
+
this.resizeObserver = new ResizeObserver(this.evaluateFit);
|
|
307
|
+
this.resizeObserver.observe(this);
|
|
308
|
+
this.evaluateFit();
|
|
309
|
+
this.syncInlineDropdowns();
|
|
310
|
+
this.syncCurrentPath();
|
|
311
|
+
}
|
|
312
|
+
connectedCallback() {
|
|
313
|
+
super.connectedCallback();
|
|
314
|
+
window.addEventListener("keydown", this.onKeyDown);
|
|
315
|
+
window.addEventListener("click", this.closeLanguageMenuOnOutsideClick);
|
|
316
|
+
window.addEventListener("click", this.closeSiteMenuOnOutsideClick);
|
|
317
|
+
this.addEventListener("product-nav-site-menu", this.onSiteMenuRequest);
|
|
318
|
+
window.addEventListener("scroll", this.onScroll, { passive: true });
|
|
319
|
+
window.addEventListener("popstate", this.syncCurrentPath);
|
|
320
|
+
this.onScroll();
|
|
321
|
+
this.narrowQuery = window.matchMedia(`(max-width: ${BREAKPOINT_TABLET}px)`);
|
|
322
|
+
this.narrowQuery.addEventListener("change", this.syncInlineDropdowns);
|
|
323
|
+
this.lightDomObserver = new MutationObserver(this.onLightDomChange);
|
|
324
|
+
this.lightDomObserver.observe(this, { childList: true, subtree: true });
|
|
325
|
+
this.startContextTrigger();
|
|
326
|
+
}
|
|
327
|
+
disconnectedCallback() {
|
|
328
|
+
this.lightDomObserver?.disconnect();
|
|
329
|
+
this.stopContextTrigger();
|
|
330
|
+
setModalOpen(this, false);
|
|
331
|
+
document.documentElement.removeAttribute(DRAWER_OPEN_ATTRIBUTE);
|
|
332
|
+
this.unlockScroll();
|
|
333
|
+
this.narrowQuery?.removeEventListener("change", this.syncInlineDropdowns);
|
|
334
|
+
this.resizeObserver?.disconnect();
|
|
335
|
+
window.removeEventListener("keydown", this.onKeyDown);
|
|
336
|
+
window.removeEventListener("click", this.closeLanguageMenuOnOutsideClick);
|
|
337
|
+
window.removeEventListener("click", this.closeSiteMenuOnOutsideClick);
|
|
338
|
+
this.removeEventListener("product-nav-site-menu", this.onSiteMenuRequest);
|
|
339
|
+
window.removeEventListener("scroll", this.onScroll);
|
|
340
|
+
window.removeEventListener("popstate", this.syncCurrentPath);
|
|
341
|
+
if (this.scrollFrame) cancelAnimationFrame(this.scrollFrame);
|
|
342
|
+
this.scrollFrame = 0;
|
|
343
|
+
super.disconnectedCallback();
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Opens the site menu as a drawer over the bar, which keeps its state underneath (see render). The
|
|
347
|
+
* drawer is the collapsed layout, so `compact` is forced on at every width, the page's modal flag is
|
|
348
|
+
* set, and focus moves to the drawer's close button.
|
|
349
|
+
*/
|
|
350
|
+
openSiteMenu(trigger) {
|
|
351
|
+
this.siteMenuTrigger = trigger;
|
|
352
|
+
this.siteMenuRequested = true;
|
|
353
|
+
this.compact = true;
|
|
354
|
+
this.mobileOpen = true;
|
|
355
|
+
setModalOpen(this, true);
|
|
356
|
+
void this.updateComplete.then(() => this.renderRoot.querySelector(".toggle")?.focus({ preventScroll: true }));
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Ends a site-menu request: the drawer closes, and the layout goes back to what the bar's own fit
|
|
360
|
+
* gives. Focus returns to the product nav's button when asked and the context row is shown.
|
|
361
|
+
*/
|
|
362
|
+
endSiteMenu(restoreFocus) {
|
|
363
|
+
if (!this.siteMenuRequested) return;
|
|
364
|
+
const trigger = this.siteMenuTrigger;
|
|
365
|
+
this.siteMenuTrigger = null;
|
|
366
|
+
this.siteMenuRequested = false;
|
|
367
|
+
setModalOpen(this, false);
|
|
368
|
+
this.mobileOpen = false;
|
|
369
|
+
this.compact = false;
|
|
370
|
+
void this.updateComplete.then(() => {
|
|
371
|
+
this.evaluateFit();
|
|
372
|
+
if (restoreFocus && this.contextActive && trigger?.isConnected) trigger.focus({ preventScroll: true });
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
/** Keeps the slotted product nav's `site-menu-open` in step with the header's site menu. */
|
|
376
|
+
syncSiteMenuOpen() {
|
|
377
|
+
for (const el of this.contextSlotElements()) {
|
|
378
|
+
const nav = el.localName === "st-product-nav" ? el : el.querySelector("st-product-nav");
|
|
379
|
+
if (nav) nav.siteMenuOpen = this.siteMenuRequested;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
/** Whether the bar has a context row: a trigger is set and `slot="context"` has content. */
|
|
383
|
+
get stacked() {
|
|
384
|
+
return !!this.contextTrigger && this.hasContext;
|
|
385
|
+
}
|
|
386
|
+
/** Layer B (the context row) is inert while it is hidden, and while a site-menu drawer is open over the bar. */
|
|
387
|
+
get layerBInert() {
|
|
388
|
+
return !this.contextActive || this.siteMenuRequested;
|
|
389
|
+
}
|
|
390
|
+
/** Starts watching the trigger element, retrying for a few seconds while the page has not rendered it. */
|
|
391
|
+
startContextTrigger() {
|
|
392
|
+
this.stopContextTrigger();
|
|
393
|
+
this.pastTrigger = false;
|
|
394
|
+
if (this.contextTrigger && typeof IntersectionObserver !== "undefined" && !this.observeContextTrigger()) {
|
|
395
|
+
this.triggerMutations = new MutationObserver(() => {
|
|
396
|
+
if (this.observeContextTrigger()) this.stopTriggerRetry();
|
|
397
|
+
});
|
|
398
|
+
this.triggerMutations.observe(document.documentElement, { childList: true, subtree: true });
|
|
399
|
+
this.triggerTimer = window.setTimeout(() => this.stopTriggerRetry(), CONTEXT_TRIGGER_TIMEOUT_MS);
|
|
400
|
+
}
|
|
401
|
+
this.updateContextActive();
|
|
402
|
+
}
|
|
403
|
+
/** Observes the trigger element if the page has it now. An invalid selector counts as not found. */
|
|
404
|
+
observeContextTrigger() {
|
|
405
|
+
let element = null;
|
|
406
|
+
try {
|
|
407
|
+
element = document.querySelector(this.contextTrigger);
|
|
408
|
+
} catch {
|
|
409
|
+
element = null;
|
|
410
|
+
}
|
|
411
|
+
if (!element) return false;
|
|
412
|
+
this.triggerObserver?.disconnect();
|
|
413
|
+
this.triggerObserver = new IntersectionObserver(this.onTriggerIntersect, {
|
|
414
|
+
rootMargin: `-${CONTEXT_BAR_HEIGHT}px 0px 0px 0px`,
|
|
415
|
+
threshold: 0
|
|
416
|
+
});
|
|
417
|
+
this.triggerObserver.observe(element);
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
stopTriggerRetry() {
|
|
421
|
+
this.triggerMutations?.disconnect();
|
|
422
|
+
this.triggerMutations = void 0;
|
|
423
|
+
window.clearTimeout(this.triggerTimer);
|
|
424
|
+
this.triggerTimer = 0;
|
|
425
|
+
}
|
|
426
|
+
stopContextTrigger() {
|
|
427
|
+
this.stopTriggerRetry();
|
|
428
|
+
this.triggerObserver?.disconnect();
|
|
429
|
+
this.triggerObserver = void 0;
|
|
430
|
+
}
|
|
431
|
+
updateContextActive() {
|
|
432
|
+
const next = this.stacked && this.pastTrigger;
|
|
433
|
+
if (next !== this.contextActive) this.setContextActive(next);
|
|
434
|
+
}
|
|
435
|
+
setContextActive(next) {
|
|
436
|
+
const focused = this.focusedLayer();
|
|
437
|
+
if (next) {
|
|
438
|
+
this.mobileOpen = false;
|
|
439
|
+
this.endSiteMenu(false);
|
|
440
|
+
}
|
|
441
|
+
this.contextActive = next;
|
|
442
|
+
this.dispatchEvent(
|
|
443
|
+
new CustomEvent("header-context-change", { detail: { active: next }, bubbles: true, composed: true })
|
|
444
|
+
);
|
|
445
|
+
if (focused === (next ? "a" : "b")) {
|
|
446
|
+
void this.updateComplete.then(() => this.focusLayer(next ? "b" : "a"));
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
/** Which layer holds the focus right now, if any. */
|
|
450
|
+
focusedLayer() {
|
|
451
|
+
const inner = this.renderRoot.activeElement;
|
|
452
|
+
if (inner) return this.renderRoot.querySelector(".layer-b")?.contains(inner) ? "b" : "a";
|
|
453
|
+
const active = document.activeElement;
|
|
454
|
+
if (!active || active === this || !this.contains(active)) return null;
|
|
455
|
+
return this.contextSlotElements().some((el) => el.contains(active)) ? "b" : "a";
|
|
456
|
+
}
|
|
457
|
+
/** Focuses the first control of the layer that is shown: the brand link or toggle (layer A), or the first rendered control of the context content (layer B). */
|
|
458
|
+
focusLayer(layer) {
|
|
459
|
+
if (layer === "a") {
|
|
460
|
+
this.layerAFocusTarget()?.focus({ preventScroll: true });
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
this.layerBFocusTarget()?.focus({ preventScroll: true });
|
|
464
|
+
}
|
|
465
|
+
/** The first rendered control of the context row: the slotted product nav's brand link. */
|
|
466
|
+
layerBFocusTarget() {
|
|
467
|
+
return renderedFocusables(this.contextSlotElements())[0] ?? null;
|
|
468
|
+
}
|
|
469
|
+
layerAFocusTarget() {
|
|
470
|
+
const candidates = [
|
|
471
|
+
...this.renderRoot.querySelectorAll(".layer-a a.brand, .layer-a .toggle"),
|
|
472
|
+
...this.querySelectorAll("a[href], button")
|
|
473
|
+
];
|
|
474
|
+
return candidates.find((el) => !el.closest('[slot="context"]') && el.getClientRects().length > 0) ?? null;
|
|
475
|
+
}
|
|
476
|
+
contextSlotElements() {
|
|
477
|
+
const slot = this.renderRoot.querySelector('slot[name="context"]');
|
|
478
|
+
return slot ? slot.assignedElements({ flatten: true }) : [];
|
|
479
|
+
}
|
|
480
|
+
/** The slotted context content is inert while layer B is inert (see layerBInert). */
|
|
481
|
+
syncContextSlot() {
|
|
482
|
+
for (const el of this.contextSlotElements()) el.toggleAttribute("inert", this.layerBInert);
|
|
483
|
+
}
|
|
484
|
+
/** The primary call to action (`cta-label` + `cta-href`), before the theme toggle. */
|
|
485
|
+
renderCta() {
|
|
486
|
+
if (!this.ctaLabel || !this.ctaHref) return nothing;
|
|
487
|
+
return html`<a part="cta" class="cta" href=${this.ctaHref}>${this.ctaLabel}</a>`;
|
|
488
|
+
}
|
|
489
|
+
renderLanguageMenu() {
|
|
490
|
+
if (this.languages.length < 2) return nothing;
|
|
491
|
+
const current = this.languages.find((lang) => lang.code === this.currentLanguageCode);
|
|
492
|
+
return html`
|
|
493
|
+
<div class="lang-menu" part="language-menu">
|
|
494
|
+
<button
|
|
495
|
+
part="language-menu-trigger"
|
|
496
|
+
type="button"
|
|
497
|
+
class="lang-trigger"
|
|
498
|
+
aria-label=${this.languageMenuLabel}
|
|
499
|
+
aria-expanded=${this.languageMenuOpen}
|
|
500
|
+
@click=${(event) => {
|
|
501
|
+
event.stopPropagation();
|
|
502
|
+
this.languageMenuOpen = !this.languageMenuOpen;
|
|
503
|
+
}}
|
|
504
|
+
>
|
|
505
|
+
<span part="language-menu-current">${current?.label ?? this.languageMenuLabel}</span>
|
|
506
|
+
${chevronIcon("down", 10)}
|
|
507
|
+
</button>
|
|
508
|
+
${this.languageMenuOpen ? html`
|
|
509
|
+
<div part="language-dropdown" class="lang-dropdown">
|
|
510
|
+
${this.languages.map(
|
|
511
|
+
(lang) => html`
|
|
512
|
+
<button
|
|
513
|
+
part="language-option"
|
|
514
|
+
type="button"
|
|
515
|
+
class="lang-option ${lang.code === this.currentLanguageCode ? "active" : ""}"
|
|
516
|
+
aria-current=${lang.code === this.currentLanguageCode}
|
|
517
|
+
@click=${() => this.onLanguageSelect(lang.code)}
|
|
518
|
+
>
|
|
519
|
+
${lang.label}
|
|
520
|
+
</button>
|
|
521
|
+
`
|
|
522
|
+
)}
|
|
523
|
+
</div>
|
|
524
|
+
` : nothing}
|
|
525
|
+
</div>
|
|
526
|
+
`;
|
|
527
|
+
}
|
|
528
|
+
onLanguageSelect(code) {
|
|
529
|
+
this.languageMenuOpen = false;
|
|
530
|
+
if (code === this.currentLanguageCode) return;
|
|
531
|
+
this.dispatchEvent(new CustomEvent("language-change", { detail: { code }, bubbles: true, composed: true }));
|
|
532
|
+
}
|
|
533
|
+
/** The header's brand link. Inert while a site-menu request is open, when the drawer shows its own copy (`drawer-head`). */
|
|
534
|
+
renderBrand(inert = false) {
|
|
535
|
+
const content = html`
|
|
536
|
+
${this.brandImageUrl ? html`<img part="brand-mark" class="brand-mark" src=${this.brandImageUrl} alt=${this.altText} /> ` : nothing}
|
|
537
|
+
<span part="brand-title" class="brand-title">${this.title}</span>
|
|
538
|
+
`;
|
|
539
|
+
const label = this.brandImageUrl ? this.altText || "Home" : this.title ? nothing : "Home";
|
|
540
|
+
return this.logoHref ? html`<a part="brand" class="brand ${this.logoClassName}" href=${this.logoHref} target=${this.logoTarget} aria-label=${label} ?inert=${inert}>${content}</a>` : html`<div part="brand" class="brand ${this.logoClassName}" ?inert=${inert}>${content}</div>`;
|
|
541
|
+
}
|
|
542
|
+
render() {
|
|
543
|
+
const showContext = this.contextActive;
|
|
544
|
+
const drawerOnly = this.siteMenuRequested;
|
|
545
|
+
return html`
|
|
546
|
+
<div class="layers ${this.stacked ? "stacked" : ""}">
|
|
547
|
+
<div class="layer layer-a ${showContext ? "" : "is-on"} ${drawerOnly ? "drawer-only" : ""}" ?inert=${showContext && !drawerOnly}>
|
|
548
|
+
${this.renderBrand(this.mobileOpen)}
|
|
549
|
+
<div class="brand-extra ${this.hasBrandExtra ? "" : "empty"}" part="brand-extra"><slot name="brand-extra" @slotchange=${this.onBrandExtraSlotChange}></slot></div>
|
|
550
|
+
|
|
551
|
+
<div
|
|
552
|
+
id="drawer"
|
|
553
|
+
class="mobile-panel"
|
|
554
|
+
part="mobile-panel"
|
|
555
|
+
role=${this.mobileOpen ? "dialog" : nothing}
|
|
556
|
+
aria-modal=${this.mobileOpen ? "true" : nothing}
|
|
557
|
+
aria-label=${this.mobileOpen ? "Site menu" : nothing}
|
|
558
|
+
@click=${() => this.closeDrawer()}
|
|
559
|
+
>
|
|
560
|
+
${this.brandImageUrl || this.title ? html`<div class="drawer-head" part="drawer-head">${this.renderBrand()}</div>` : nothing}
|
|
561
|
+
<nav class="menu-row" part="menu-row" aria-label="Main"><slot name="menu" @slotchange=${this.onMenuSlotChange}></slot></nav>
|
|
562
|
+
<div class="utility-row ${this.hasUtility || this.languages.length >= 2 || this.ctaLabel ? "" : "empty"}" part="utility-row">
|
|
563
|
+
${this.renderLanguageMenu()}${this.renderCta()}<slot name="utility" @slotchange=${this.onUtilitySlotChange}></slot>
|
|
564
|
+
</div>
|
|
565
|
+
</div>
|
|
566
|
+
|
|
567
|
+
${this.siteMenuRequested ? html`<div class="site-menu-backdrop" part="site-menu-backdrop" @click=${() => this.closeDrawer()}></div>` : nothing}
|
|
568
|
+
|
|
569
|
+
<button
|
|
570
|
+
part="toggle"
|
|
571
|
+
type="button"
|
|
572
|
+
class="toggle"
|
|
573
|
+
aria-label=${this.mobileOpen ? "Close menu" : "Open menu"}
|
|
574
|
+
aria-expanded=${this.mobileOpen}
|
|
575
|
+
aria-controls="drawer"
|
|
576
|
+
@click=${() => this.mobileOpen ? this.closeDrawer() : this.mobileOpen = true}
|
|
577
|
+
>
|
|
578
|
+
<slot name="menu-icon" ?hidden=${this.mobileOpen}>${menuIcon()}</slot>
|
|
579
|
+
<slot name="close-icon" ?hidden=${!this.mobileOpen}>${closeIcon()}</slot>
|
|
580
|
+
</button>
|
|
581
|
+
</div>
|
|
582
|
+
|
|
583
|
+
<div class="layer layer-b ${showContext ? "is-on" : ""}" ?inert=${this.layerBInert}>
|
|
584
|
+
<slot name="context" @slotchange=${this.onContextSlotChange}></slot>
|
|
585
|
+
</div>
|
|
586
|
+
</div>
|
|
587
|
+
`;
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
Header.styles = css`
|
|
591
|
+
${controlBaseStyles}
|
|
592
|
+
|
|
593
|
+
:host {
|
|
594
|
+
display: flex;
|
|
595
|
+
align-items: center;
|
|
596
|
+
/* The containing block of an st-dropdown[mega] panel, which spans the bar. */
|
|
597
|
+
position: relative;
|
|
598
|
+
/* Spacing between brand, menu and utility controls is the rows' own margins, not a
|
|
599
|
+
flex gap. */
|
|
600
|
+
gap: 0;
|
|
601
|
+
/* !important on the box-model properties only: a shadow host's own :host rule always loses
|
|
602
|
+
* to ANY rule the consuming page has for the same property on that element, regardless of
|
|
603
|
+
* specificity (see st-footer's own padding/border-top for the same fix). Without
|
|
604
|
+
* box-sizing: border-box pinned here, a page that also sets its own padding on this host
|
|
605
|
+
* (e.g. a p-2 utility class) adds that padding on TOP of the fixed 64px height instead of
|
|
606
|
+
* being absorbed inside it — which is exactly what happens in Template Hub, the one app that
|
|
607
|
+
* doesn't load Tailwind Preflight (Preflight's own box-sizing: border-box reset is what
|
|
608
|
+
* silently keeps every other app's header at the intended 64px). */
|
|
609
|
+
box-sizing: border-box !important;
|
|
610
|
+
padding: 0 2rem !important;
|
|
611
|
+
height: 64px !important;
|
|
612
|
+
background: var(--st-color-surface);
|
|
613
|
+
border-bottom: 1px solid var(--st-color-border) !important;
|
|
614
|
+
${rootFontFamilyStyles}
|
|
615
|
+
color: var(--st-color-text-strong);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
:host([sticky]) {
|
|
619
|
+
position: sticky;
|
|
620
|
+
top: 0;
|
|
621
|
+
z-index: 100;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/* Toggle icons: a host's slotted icon, else the built-in glyph; only the current state's shows. */
|
|
625
|
+
.toggle slot {
|
|
626
|
+
display: contents;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
.toggle slot[hidden] {
|
|
630
|
+
display: none;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.toggle ::slotted(*) {
|
|
634
|
+
flex: none;
|
|
635
|
+
width: var(--st-header-toggle-icon-size, 20px);
|
|
636
|
+
height: var(--st-header-toggle-icon-size, 20px);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
:host([inverted]) {
|
|
640
|
+
background: var(--st-color-text-strong);
|
|
641
|
+
color: var(--st-color-on-solid);
|
|
642
|
+
border-bottom-color: transparent;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
:host([sticky]),
|
|
646
|
+
:host([transparent]),
|
|
647
|
+
:host([hide-on-scroll]) {
|
|
648
|
+
transition:
|
|
649
|
+
background-color 0.3s ease,
|
|
650
|
+
color 0.3s ease,
|
|
651
|
+
box-shadow 0.3s ease,
|
|
652
|
+
border-color 0.3s ease,
|
|
653
|
+
transform 0.3s ease;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/* Once the page has scrolled, a sticky/fixed header lifts off the content. */
|
|
657
|
+
:host([scrolled][sticky]),
|
|
658
|
+
:host([scrolled][transparent]),
|
|
659
|
+
:host([scrolled][hide-on-scroll]) {
|
|
660
|
+
box-shadow: 0 4px 16px var(--st-shadow-color-md);
|
|
661
|
+
border-bottom-color: transparent !important;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/* transparent: fixed over the top of the page (e.g. a dark hero), see-through until scrolled. */
|
|
665
|
+
:host([transparent]) {
|
|
666
|
+
position: fixed;
|
|
667
|
+
top: 0;
|
|
668
|
+
left: 0;
|
|
669
|
+
right: 0;
|
|
670
|
+
z-index: 100;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
:host([transparent]:not([scrolled]):not([mobile-open])) {
|
|
674
|
+
background: transparent;
|
|
675
|
+
border-bottom-color: transparent !important;
|
|
676
|
+
color: var(--st-header-transparent-color, var(--st-color-on-solid));
|
|
677
|
+
/* Read by st-menu-item / st-dropdown triggers in the bar (their panels reset them). */
|
|
678
|
+
--st-nav-color: var(--st-header-transparent-color, var(--st-color-on-solid));
|
|
679
|
+
--st-nav-hover-bg: color-mix(in oklch, currentColor 14%, transparent);
|
|
680
|
+
/* The current-page underline follows the bar's text colour here, since the accent may not read on a hero. */
|
|
681
|
+
--st-nav-active-line: currentColor;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
:host([transparent]:not([scrolled]):not([mobile-open])) .utility-row {
|
|
685
|
+
border-left-color: color-mix(in oklch, currentColor 35%, transparent);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
:host([transparent]:not([scrolled]):not([mobile-open])) .lang-trigger {
|
|
689
|
+
color: inherit;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
:host([transparent]:not([scrolled]):not([mobile-open])) .lang-trigger:hover,
|
|
693
|
+
:host([transparent]:not([scrolled]):not([mobile-open])) .toggle:hover {
|
|
694
|
+
background: color-mix(in oklch, currentColor 14%, transparent);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/* hide-on-scroll: slides away while scrolling down, back as soon as the user scrolls up. */
|
|
698
|
+
:host([hide-on-scroll]:not([sticky]):not([transparent])) {
|
|
699
|
+
position: sticky;
|
|
700
|
+
top: 0;
|
|
701
|
+
z-index: 100;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
:host([scroll-hidden]:not([mobile-open])) {
|
|
705
|
+
transform: translateY(-100%);
|
|
706
|
+
box-shadow: none;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.brand {
|
|
710
|
+
display: flex;
|
|
711
|
+
align-items: center;
|
|
712
|
+
gap: var(--st-space-10);
|
|
713
|
+
flex: none;
|
|
714
|
+
text-decoration: none;
|
|
715
|
+
color: inherit;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
.brand-mark {
|
|
719
|
+
width: var(--st-size-28);
|
|
720
|
+
height: var(--st-size-28);
|
|
721
|
+
border-radius: var(--st-radius-sm);
|
|
722
|
+
flex: none;
|
|
723
|
+
object-fit: contain;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.brand-title {
|
|
727
|
+
font-size: var(--st-text-xl);
|
|
728
|
+
font-weight: var(--st-font-weight-bold);
|
|
729
|
+
letter-spacing: var(--st-tracking-tight);
|
|
730
|
+
white-space: nowrap;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/* Optional slot="brand-extra" (e.g. an edition badge) right after the brand. Outside the
|
|
734
|
+
mobile panel, so it stays beside the logo at every width; no box at all when empty. */
|
|
735
|
+
.brand-extra {
|
|
736
|
+
display: flex;
|
|
737
|
+
align-items: center;
|
|
738
|
+
gap: var(--st-space-8);
|
|
739
|
+
flex: none;
|
|
740
|
+
margin-left: var(--st-space-10);
|
|
741
|
+
white-space: nowrap;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
.brand-extra.empty {
|
|
745
|
+
display: none;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/* Desktop: a transparent pass-through so .menu-row/.utility-row lay out directly as :host's
|
|
749
|
+
own flex children. Mobile (see below) turns this into the actual overlay box. */
|
|
750
|
+
.mobile-panel {
|
|
751
|
+
display: contents;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/* Desktop layout: the menu starts 4rem after the brand and takes the
|
|
755
|
+
free width; the utility controls sit after a vertical divider. Between 901px and 1200px the
|
|
756
|
+
gaps tighten so the menu still fits down to the 900px hamburger breakpoint. */
|
|
757
|
+
.menu-row {
|
|
758
|
+
flex: 1;
|
|
759
|
+
display: flex;
|
|
760
|
+
align-items: center;
|
|
761
|
+
gap: 1.5rem;
|
|
762
|
+
min-width: 0;
|
|
763
|
+
margin-left: 4rem;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
.utility-row {
|
|
767
|
+
flex: none;
|
|
768
|
+
display: flex;
|
|
769
|
+
align-items: center;
|
|
770
|
+
gap: 1rem;
|
|
771
|
+
margin-left: 1.5rem;
|
|
772
|
+
padding-left: 1.5rem;
|
|
773
|
+
border-left: 1px solid var(--st-color-border);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
:host([inverted]) .utility-row {
|
|
777
|
+
border-left-color: currentColor;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.utility-row.empty {
|
|
781
|
+
display: none;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
@media (max-width: 1200px) {
|
|
785
|
+
:host {
|
|
786
|
+
padding: 0 1rem !important;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.menu-row {
|
|
790
|
+
margin-left: 0.5rem;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
.utility-row {
|
|
794
|
+
margin-left: 0.5rem;
|
|
795
|
+
padding-left: 0.5rem;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
.toggle {
|
|
800
|
+
display: none;
|
|
801
|
+
align-items: center;
|
|
802
|
+
justify-content: center;
|
|
803
|
+
flex: none;
|
|
804
|
+
position: relative;
|
|
805
|
+
z-index: 1002;
|
|
806
|
+
/* A 44px touch target; the glyph inside keeps its 20px size. */
|
|
807
|
+
width: 44px;
|
|
808
|
+
height: 44px;
|
|
809
|
+
margin-left: auto;
|
|
810
|
+
padding: 0.5rem;
|
|
811
|
+
border: 0;
|
|
812
|
+
border-radius: var(--st-radius-sm);
|
|
813
|
+
background: transparent;
|
|
814
|
+
color: inherit;
|
|
815
|
+
cursor: pointer;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
.toggle:hover {
|
|
819
|
+
background: var(--st-color-hover);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.lang-menu {
|
|
823
|
+
position: relative;
|
|
824
|
+
flex: none;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
.lang-trigger {
|
|
828
|
+
${buttonResetStyles}
|
|
829
|
+
display: flex;
|
|
830
|
+
align-items: center;
|
|
831
|
+
gap: var(--st-space-4);
|
|
832
|
+
height: var(--st-size-32);
|
|
833
|
+
padding: 0 var(--st-space-8);
|
|
834
|
+
border-radius: var(--st-radius-sm);
|
|
835
|
+
font-size: var(--st-text-base);
|
|
836
|
+
font-weight: var(--st-font-weight-semibold);
|
|
837
|
+
color: var(--st-color-text-muted);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.lang-trigger:hover {
|
|
841
|
+
background: var(--st-color-hover);
|
|
842
|
+
color: var(--st-color-text-strong);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
.lang-dropdown {
|
|
846
|
+
${dropdownPanelStyles}
|
|
847
|
+
min-width: 140px;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.lang-option {
|
|
851
|
+
${buttonResetStyles}
|
|
852
|
+
display: block;
|
|
853
|
+
width: 100%;
|
|
854
|
+
padding: var(--st-space-10) var(--st-space-14);
|
|
855
|
+
font-size: var(--st-text-base);
|
|
856
|
+
font-weight: var(--st-font-weight-semibold);
|
|
857
|
+
text-align: left;
|
|
858
|
+
color: inherit;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
.lang-option:hover {
|
|
862
|
+
background: var(--st-color-hover);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
.lang-option.active {
|
|
866
|
+
color: var(--st-color-accent-text);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/* Primary call to action in the utility row, before the theme toggle. Solid accent, so it reads on both themes. */
|
|
870
|
+
.cta {
|
|
871
|
+
display: inline-flex;
|
|
872
|
+
align-items: center;
|
|
873
|
+
justify-content: center;
|
|
874
|
+
flex: none;
|
|
875
|
+
box-sizing: border-box;
|
|
876
|
+
min-height: 44px;
|
|
877
|
+
padding: 0 1.25rem;
|
|
878
|
+
border-radius: 9999px;
|
|
879
|
+
background: var(--st-color-accent);
|
|
880
|
+
color: var(--st-color-on-solid);
|
|
881
|
+
font-size: var(--st-text-base);
|
|
882
|
+
font-weight: var(--st-font-weight-semibold);
|
|
883
|
+
text-decoration: none;
|
|
884
|
+
white-space: nowrap;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
.cta:hover {
|
|
888
|
+
background: color-mix(in oklch, var(--st-color-accent) 88%, var(--st-color-text-strong));
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/* Hamburger layout: a full-screen layer under the toggle, menu above
|
|
892
|
+
the utility controls, the menu's dropdowns opening in the flow (see syncInlineDropdowns). */
|
|
893
|
+
@media (max-width: ${BREAKPOINT_TABLET}px) {
|
|
894
|
+
:host {
|
|
895
|
+
padding: 0 1rem !important;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
.mobile-panel {
|
|
899
|
+
display: none;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
.toggle {
|
|
903
|
+
display: inline-flex;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
:host([mobile-open]) .mobile-panel {
|
|
907
|
+
display: flex;
|
|
908
|
+
flex-direction: column;
|
|
909
|
+
gap: 2rem;
|
|
910
|
+
position: fixed;
|
|
911
|
+
inset: 0;
|
|
912
|
+
background: var(--st-color-surface);
|
|
913
|
+
padding: 0 1.5rem 2rem;
|
|
914
|
+
overflow-y: auto;
|
|
915
|
+
overscroll-behavior: contain;
|
|
916
|
+
z-index: 1001;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
:host([mobile-open]) .menu-row,
|
|
920
|
+
:host([mobile-open]) .utility-row {
|
|
921
|
+
flex-direction: column;
|
|
922
|
+
align-items: stretch;
|
|
923
|
+
gap: 1.5rem;
|
|
924
|
+
margin-left: 0;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
:host([mobile-open]) .utility-row {
|
|
928
|
+
padding: 1.5rem 0 0;
|
|
929
|
+
border-left: 0;
|
|
930
|
+
border-top: 1px solid var(--st-color-border);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/* Apps usually slot one flex-row wrapper per slot; stack its items as the source does. */
|
|
934
|
+
:host([mobile-open]) ::slotted([slot='menu']),
|
|
935
|
+
:host([mobile-open]) ::slotted([slot='utility']) {
|
|
936
|
+
flex-direction: column !important;
|
|
937
|
+
align-items: stretch !important;
|
|
938
|
+
margin: 0 !important;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/* The same collapsed layout, switched on by script when the menu no longer fits the bar at
|
|
943
|
+
desktop width (see evaluateFit) — without it the menu spills over the utility controls. A site-menu
|
|
944
|
+
request (see openSiteMenu) switches it on the same way at every width. */
|
|
945
|
+
:host([compact]) .mobile-panel {
|
|
946
|
+
display: none;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
:host([compact]) .toggle {
|
|
950
|
+
display: inline-flex;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
:host([compact][mobile-open]) .mobile-panel {
|
|
954
|
+
display: flex;
|
|
955
|
+
flex-direction: column;
|
|
956
|
+
gap: 2rem;
|
|
957
|
+
position: fixed;
|
|
958
|
+
inset: 0;
|
|
959
|
+
background: var(--st-color-surface);
|
|
960
|
+
padding: 0 1.5rem 2rem;
|
|
961
|
+
overflow-y: auto;
|
|
962
|
+
overscroll-behavior: contain;
|
|
963
|
+
z-index: 1001;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
:host([compact][mobile-open]) .menu-row,
|
|
967
|
+
:host([compact][mobile-open]) .utility-row {
|
|
968
|
+
flex-direction: column;
|
|
969
|
+
align-items: stretch;
|
|
970
|
+
gap: 1.5rem;
|
|
971
|
+
margin-left: 0;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
:host([compact][mobile-open]) .utility-row {
|
|
975
|
+
padding: 1.5rem 0 0;
|
|
976
|
+
border-left: 0;
|
|
977
|
+
border-top: 1px solid var(--st-color-border);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
:host([compact][mobile-open]) ::slotted([slot='menu']),
|
|
981
|
+
:host([compact][mobile-open]) ::slotted([slot='utility']) {
|
|
982
|
+
flex-direction: column !important;
|
|
983
|
+
align-items: stretch !important;
|
|
984
|
+
margin: 0 !important;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/* Context mode. Without context content both layers are transparent wrappers (display: contents),
|
|
988
|
+
so the header lays out exactly as before. With it, layer A (the site header) and layer B (the
|
|
989
|
+
context row) share one grid cell, and the inactive one fades out and is hidden. */
|
|
990
|
+
.layers,
|
|
991
|
+
.layers .layer {
|
|
992
|
+
display: contents;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.layers .layer-b {
|
|
996
|
+
display: none;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/* A context-trigger bar is sticky by itself (a transparent one keeps its own fixed positioning). */
|
|
1000
|
+
:host([context-trigger]:not([transparent])) {
|
|
1001
|
+
position: sticky;
|
|
1002
|
+
top: 0;
|
|
1003
|
+
z-index: 100;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.layers.stacked {
|
|
1007
|
+
display: grid;
|
|
1008
|
+
flex: 1 1 auto;
|
|
1009
|
+
min-width: 0;
|
|
1010
|
+
height: 100%;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
.layers.stacked .layer {
|
|
1014
|
+
grid-area: 1 / 1;
|
|
1015
|
+
display: flex;
|
|
1016
|
+
align-items: center;
|
|
1017
|
+
min-width: 0;
|
|
1018
|
+
height: 100%;
|
|
1019
|
+
opacity: 1;
|
|
1020
|
+
visibility: visible;
|
|
1021
|
+
/* Outgoing layer: fades out, and its visibility only drops once the fade has finished. */
|
|
1022
|
+
transition:
|
|
1023
|
+
opacity 180ms ease,
|
|
1024
|
+
visibility 0s linear 180ms;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/* Incoming layer: visible at once, then fades in. */
|
|
1028
|
+
.layers.stacked .layer.is-on {
|
|
1029
|
+
transition:
|
|
1030
|
+
opacity 180ms ease,
|
|
1031
|
+
visibility 0s linear 0s;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
.layers.stacked .layer:not(.is-on) {
|
|
1035
|
+
opacity: 0;
|
|
1036
|
+
visibility: hidden;
|
|
1037
|
+
pointer-events: none;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/* Site-menu request (see openSiteMenu): layer A keeps only what the drawer needs. Its bar row is hidden
|
|
1041
|
+
by visibility, and its opacity is 1, since a faded ancestor would fade the drawer too. The drawer, the
|
|
1042
|
+
toggle (the close control) and the backdrop are shown again. Layer B stays visible underneath. */
|
|
1043
|
+
.layers.stacked .layer-a.drawer-only {
|
|
1044
|
+
opacity: 1;
|
|
1045
|
+
visibility: hidden;
|
|
1046
|
+
pointer-events: none;
|
|
1047
|
+
transition: none;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
.layers.stacked .layer-a.drawer-only .mobile-panel,
|
|
1051
|
+
.layers.stacked .layer-a.drawer-only .toggle,
|
|
1052
|
+
.layers.stacked .layer-a.drawer-only .site-menu-backdrop {
|
|
1053
|
+
visibility: visible;
|
|
1054
|
+
pointer-events: auto;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
.layers.stacked .layer-b {
|
|
1058
|
+
align-items: center;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
.layers.stacked .layer-b slot {
|
|
1062
|
+
display: contents;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
.layers.stacked .layer-b ::slotted(*) {
|
|
1066
|
+
flex: 1 1 auto;
|
|
1067
|
+
min-width: 0;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
/* The context bar is solid, even when the header is transparent over a hero. */
|
|
1071
|
+
:host([context-active]) {
|
|
1072
|
+
background: var(--st-color-surface) !important;
|
|
1073
|
+
border-bottom-color: var(--st-color-border) !important;
|
|
1074
|
+
color: var(--st-color-text-strong) !important;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/* Site-menu request (see openSiteMenu). The drawer's top row carries the brand, aligned with the bar's
|
|
1078
|
+
64px row, so the toggle sits beside it. The drawer is a right-side panel over a backdrop above 768px;
|
|
1079
|
+
below that it stays the full-screen drawer. Only a request uses these rules, not normal mobile use. */
|
|
1080
|
+
.drawer-head {
|
|
1081
|
+
display: none;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/* The drawer's own top row: the brand, on the bar's row. Pulled back to the bar's 1rem edge, where the toggle sits. */
|
|
1085
|
+
:host([mobile-open]) .drawer-head {
|
|
1086
|
+
display: flex;
|
|
1087
|
+
align-items: center;
|
|
1088
|
+
flex: none;
|
|
1089
|
+
height: 64px;
|
|
1090
|
+
min-width: 0;
|
|
1091
|
+
margin: 0 -0.5rem;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
:host([site-menu-request][compact][mobile-open]) .mobile-panel {
|
|
1095
|
+
padding: 0 1.5rem 2rem;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
.site-menu-backdrop {
|
|
1099
|
+
display: none;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
@media (min-width: 769px) {
|
|
1103
|
+
:host([site-menu-request][compact][mobile-open]) .mobile-panel {
|
|
1104
|
+
inset: 0 0 0 auto;
|
|
1105
|
+
width: min(400px, 100vw);
|
|
1106
|
+
height: 100%;
|
|
1107
|
+
box-shadow: -8px 0 24px var(--st-shadow-color-md);
|
|
1108
|
+
animation: st-header-slide-in 200ms ease;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
:host([site-menu-request]) .site-menu-backdrop {
|
|
1112
|
+
display: block;
|
|
1113
|
+
position: fixed;
|
|
1114
|
+
inset: 0;
|
|
1115
|
+
z-index: 1000;
|
|
1116
|
+
background: rgba(1, 29, 65, 0.45);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
@keyframes st-header-slide-in {
|
|
1121
|
+
from {
|
|
1122
|
+
transform: translateX(100%);
|
|
1123
|
+
}
|
|
1124
|
+
to {
|
|
1125
|
+
transform: none;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1130
|
+
.layers.stacked .layer {
|
|
1131
|
+
transition: none !important;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1136
|
+
:host,
|
|
1137
|
+
.mobile-panel {
|
|
1138
|
+
animation: none !important;
|
|
1139
|
+
transition: none !important;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
`;
|
|
1143
|
+
__decorateClass([
|
|
1144
|
+
property({ type: String })
|
|
1145
|
+
], Header.prototype, "title", 2);
|
|
1146
|
+
__decorateClass([
|
|
1147
|
+
property({ type: String, attribute: "brand-image-url" })
|
|
1148
|
+
], Header.prototype, "brandImageUrl", 2);
|
|
1149
|
+
__decorateClass([
|
|
1150
|
+
property({ type: String, attribute: "current-path" })
|
|
1151
|
+
], Header.prototype, "currentPath", 2);
|
|
1152
|
+
__decorateClass([
|
|
1153
|
+
property({ type: String, attribute: "alt-text" })
|
|
1154
|
+
], Header.prototype, "altText", 2);
|
|
1155
|
+
__decorateClass([
|
|
1156
|
+
property({ type: String })
|
|
1157
|
+
], Header.prototype, "logoHref", 2);
|
|
1158
|
+
__decorateClass([
|
|
1159
|
+
property({ type: String, attribute: "logo-target" })
|
|
1160
|
+
], Header.prototype, "logoTarget", 2);
|
|
1161
|
+
__decorateClass([
|
|
1162
|
+
property({ type: String, attribute: "logo-class-name" })
|
|
1163
|
+
], Header.prototype, "logoClassName", 2);
|
|
1164
|
+
__decorateClass([
|
|
1165
|
+
property({ type: Boolean, reflect: true })
|
|
1166
|
+
], Header.prototype, "inverted", 2);
|
|
1167
|
+
__decorateClass([
|
|
1168
|
+
property({ type: Boolean, reflect: true })
|
|
1169
|
+
], Header.prototype, "sticky", 2);
|
|
1170
|
+
__decorateClass([
|
|
1171
|
+
property({ type: Boolean, reflect: true })
|
|
1172
|
+
], Header.prototype, "transparent", 2);
|
|
1173
|
+
__decorateClass([
|
|
1174
|
+
property({ type: Boolean, reflect: true, attribute: "hide-on-scroll" })
|
|
1175
|
+
], Header.prototype, "hideOnScroll", 2);
|
|
1176
|
+
__decorateClass([
|
|
1177
|
+
property({ type: Number, attribute: "scroll-threshold" })
|
|
1178
|
+
], Header.prototype, "scrollThreshold", 2);
|
|
1179
|
+
__decorateClass([
|
|
1180
|
+
property({ type: Boolean, reflect: true })
|
|
1181
|
+
], Header.prototype, "scrolled", 2);
|
|
1182
|
+
__decorateClass([
|
|
1183
|
+
property({ type: Boolean, reflect: true, attribute: "scroll-hidden" })
|
|
1184
|
+
], Header.prototype, "scrollHidden", 2);
|
|
1185
|
+
__decorateClass([
|
|
1186
|
+
property({ attribute: false })
|
|
1187
|
+
], Header.prototype, "languages", 2);
|
|
1188
|
+
__decorateClass([
|
|
1189
|
+
property({ type: String, attribute: "current-language-code" })
|
|
1190
|
+
], Header.prototype, "currentLanguageCode", 2);
|
|
1191
|
+
__decorateClass([
|
|
1192
|
+
property({ type: String, attribute: "language-menu-label" })
|
|
1193
|
+
], Header.prototype, "languageMenuLabel", 2);
|
|
1194
|
+
__decorateClass([
|
|
1195
|
+
property({ type: Boolean, reflect: true, attribute: "mobile-open" })
|
|
1196
|
+
], Header.prototype, "mobileOpen", 2);
|
|
1197
|
+
__decorateClass([
|
|
1198
|
+
property({ type: Boolean, reflect: true })
|
|
1199
|
+
], Header.prototype, "compact", 2);
|
|
1200
|
+
__decorateClass([
|
|
1201
|
+
property({ type: String, attribute: "cta-label" })
|
|
1202
|
+
], Header.prototype, "ctaLabel", 2);
|
|
1203
|
+
__decorateClass([
|
|
1204
|
+
property({ type: String, attribute: "cta-href" })
|
|
1205
|
+
], Header.prototype, "ctaHref", 2);
|
|
1206
|
+
__decorateClass([
|
|
1207
|
+
state()
|
|
1208
|
+
], Header.prototype, "languageMenuOpen", 2);
|
|
1209
|
+
__decorateClass([
|
|
1210
|
+
state()
|
|
1211
|
+
], Header.prototype, "hasUtility", 2);
|
|
1212
|
+
__decorateClass([
|
|
1213
|
+
state()
|
|
1214
|
+
], Header.prototype, "hasBrandExtra", 2);
|
|
1215
|
+
__decorateClass([
|
|
1216
|
+
property({ type: String, attribute: "context-trigger" })
|
|
1217
|
+
], Header.prototype, "contextTrigger", 2);
|
|
1218
|
+
__decorateClass([
|
|
1219
|
+
property({ type: Boolean, reflect: true, attribute: "context-active" })
|
|
1220
|
+
], Header.prototype, "contextActive", 2);
|
|
1221
|
+
__decorateClass([
|
|
1222
|
+
state()
|
|
1223
|
+
], Header.prototype, "hasContext", 2);
|
|
1224
|
+
__decorateClass([
|
|
1225
|
+
state()
|
|
1226
|
+
], Header.prototype, "siteMenuRequested", 2);
|
|
1227
|
+
Header = __decorateClass([
|
|
1228
|
+
customElement("st-header")
|
|
1229
|
+
], Header);
|
|
1230
|
+
|
|
1231
|
+
export {
|
|
1232
|
+
Header
|
|
1233
|
+
};
|
|
1234
|
+
//# sourceMappingURL=chunk-WAYHHL4J.js.map
|