weg-shared-layout 0.0.40 → 0.0.42
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/LICENSE +21 -21
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/weg-footer_2.cjs.entry.js +449 -32
- package/dist/cjs/weg-shared-layout.cjs.js +1 -1
- package/dist/collection/components/weg-footer/icons/instagram.svg +5 -5
- package/dist/collection/components/weg-footer/icons/linkedin.svg +3 -3
- package/dist/collection/components/weg-footer/icons/tiktok.svg +3 -3
- package/dist/collection/components/weg-footer/icons/youtube.svg +4 -4
- package/dist/collection/components/weg-footer/weg-footer.css +263 -263
- package/dist/collection/components/weg-footer/weg-footer.js +2 -2
- package/dist/collection/components/weg-header/weg-header.css +533 -483
- package/dist/collection/components/weg-header/weg-header.js +348 -32
- package/dist/collection/styles/shared.css +104 -103
- package/dist/collection/utils/nav-active.js +121 -0
- package/dist/components/weg-footer.js +1 -1
- package/dist/components/weg-header.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/weg-footer_2.entry.js +449 -32
- package/dist/esm/weg-shared-layout.js +1 -1
- package/dist/types/components/weg-header/weg-header.d.ts +37 -0
- package/dist/types/components.d.ts +9 -0
- package/dist/types/utils/nav-active.d.ts +20 -0
- package/dist/weg-shared-layout/p-877ccc54.entry.js +1 -0
- package/dist/weg-shared-layout/weg-shared-layout.esm.js +1 -1
- package/docs/README.md +51 -49
- package/docs/angular.md +451 -400
- package/docs/nextjs.md +312 -302
- package/docs/publishing.md +65 -65
- package/docs/react.md +287 -257
- package/docs/vanilla.md +155 -124
- package/package.json +81 -81
- package/readme.md +108 -97
- package/src/constants/breakpoints.ts +7 -7
- package/src/styles/container.css +60 -60
- package/dist/weg-shared-layout/p-6f83b24a.entry.js +0 -1
- /package/dist/types/Users/{tan/Documents/code → rakin/Documents/WEG/repos}/weg-shared-layout/.stencil/test-utils/axe.d.ts +0 -0
- /package/dist/types/Users/{tan/Documents/code → rakin/Documents/WEG/repos}/weg-shared-layout/.stencil/test-utils/viewport.d.ts +0 -0
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { h } from "@stencil/core";
|
|
2
2
|
import { findAuthMenuLink, isMenuGroup, isNonEmptyString, normalizeHeaderMenu, parseJsonProp, } from "../../utils/layout";
|
|
3
3
|
import { WEG_HOVER_CAPABLE_MEDIA_QUERY, WEG_MD_MEDIA_QUERY } from "../../constants/breakpoints";
|
|
4
|
+
import { isAriaCurrentNavHref, isNavLinkActive, resolveAriaCurrentNavKey, } from "../../utils/nav-active";
|
|
4
5
|
import { LOGO_SRC } from "./logo-data";
|
|
5
6
|
const WEG_HOME = 'https://www.warwickemploymentgroup.com';
|
|
6
7
|
const DEFAULT_ACCOUNT_HOME = 'https://account.warwickemploymentgroup.com';
|
|
8
|
+
/** Stay visible while scrolling down through the first half of the viewport. */
|
|
9
|
+
const HEADER_SCROLL_HIDE_THRESHOLD_VH = 0.5;
|
|
10
|
+
const HEADER_SCROLL_DIRECTION_DELTA = 5;
|
|
7
11
|
const DEFAULT_LOGO_HREF = `${WEG_HOME}/`;
|
|
8
12
|
/** Placeholder href — sign-out is handled via `wegAuthClick`, not navigation. */
|
|
9
13
|
const SIGN_OUT_HREF = '#';
|
|
14
|
+
const MAIN_NAV_LABEL = 'Main navigation';
|
|
15
|
+
const OPEN_MAIN_NAV_LABEL = 'Open main navigation';
|
|
16
|
+
const CLOSE_MAIN_NAV_LABEL = 'Close main navigation';
|
|
10
17
|
function normalizeAccountBase(input) {
|
|
11
18
|
const base = input?.trim() || DEFAULT_ACCOUNT_HOME;
|
|
12
19
|
return base.replace(/\/$/, '');
|
|
@@ -91,7 +98,7 @@ function ToggleIcon({ expanded }) {
|
|
|
91
98
|
return (h("span", { class: "toggle-icon", "aria-hidden": "true" }, expanded ? (h("svg", { viewBox: "0 0 24 24", width: "14", height: "14", fill: "none" }, h("path", { d: "M6 12h12", stroke: "currentColor", "stroke-width": "2.5", "stroke-linecap": "round" }))) : (h("svg", { viewBox: "0 0 24 24", width: "14", height: "14", fill: "none" }, h("path", { d: "M12 6v12M6 12h12", stroke: "currentColor", "stroke-width": "2.5", "stroke-linecap": "round" })))));
|
|
92
99
|
}
|
|
93
100
|
function Logo({ href, src }) {
|
|
94
|
-
return (h("a", { class: "logo-link", href: href, "aria-label": "Home" }, h("img", { class: "logo", src: src, alt: "
|
|
101
|
+
return (h("a", { class: "logo-link", href: href, "aria-label": "Home" }, h("img", { class: "logo", src: src, alt: "Warwick Employment Group logo", width: "225", height: "83" })));
|
|
95
102
|
}
|
|
96
103
|
export class WegHeader {
|
|
97
104
|
/**
|
|
@@ -124,6 +131,11 @@ export class WegHeader {
|
|
|
124
131
|
* Defaults to production when omitted.
|
|
125
132
|
*/
|
|
126
133
|
accountBaseUrl;
|
|
134
|
+
/**
|
|
135
|
+
* Current page path from the host application (e.g. `/career-advice/my-article`).
|
|
136
|
+
* Include a query string when matching dropdown links that use search params.
|
|
137
|
+
*/
|
|
138
|
+
currentPath;
|
|
127
139
|
/**
|
|
128
140
|
* Fired when the user clicks Sign in or Sign out.
|
|
129
141
|
* Call `event.preventDefault()` in the host to handle navigation/logout yourself.
|
|
@@ -133,9 +145,16 @@ export class WegHeader {
|
|
|
133
145
|
resolved = EMPTY_HEADER;
|
|
134
146
|
menuOpen = false;
|
|
135
147
|
openDropdown = null;
|
|
148
|
+
headerHidden = false;
|
|
136
149
|
boundHandleDocumentClick = this.handleDocumentClick.bind(this);
|
|
137
150
|
boundHandleViewportChange = this.handleViewportChange.bind(this);
|
|
138
151
|
boundHandleHoverCapabilityChange = this.handleHoverCapabilityChange.bind(this);
|
|
152
|
+
boundHandleScroll = this.handleScroll.bind(this);
|
|
153
|
+
headerResizeObserver;
|
|
154
|
+
lastScrollY = 0;
|
|
155
|
+
scrollTicking = false;
|
|
156
|
+
bodyScrollLocked = false;
|
|
157
|
+
bodyScrollLockY = 0;
|
|
139
158
|
hoverCloseTimer = null;
|
|
140
159
|
hoverOpenEnabled = false;
|
|
141
160
|
desktopMediaQuery;
|
|
@@ -156,15 +175,69 @@ export class WegHeader {
|
|
|
156
175
|
this.updateHoverDropdownEnabled();
|
|
157
176
|
this.handleViewportChange();
|
|
158
177
|
}
|
|
178
|
+
componentDidLoad() {
|
|
179
|
+
const header = this.el.shadowRoot?.querySelector('.header');
|
|
180
|
+
if (!header)
|
|
181
|
+
return;
|
|
182
|
+
this.headerResizeObserver = new ResizeObserver(() => this.syncHeaderHeight());
|
|
183
|
+
this.headerResizeObserver.observe(header);
|
|
184
|
+
this.syncHeaderHeight();
|
|
185
|
+
this.lastScrollY = window.scrollY;
|
|
186
|
+
this.updateHeaderVisibility();
|
|
187
|
+
}
|
|
159
188
|
connectedCallback() {
|
|
160
189
|
document.addEventListener('click', this.boundHandleDocumentClick);
|
|
190
|
+
window.addEventListener('scroll', this.boundHandleScroll, { passive: true });
|
|
161
191
|
}
|
|
162
192
|
disconnectedCallback() {
|
|
163
193
|
document.removeEventListener('click', this.boundHandleDocumentClick);
|
|
194
|
+
window.removeEventListener('scroll', this.boundHandleScroll);
|
|
195
|
+
this.unlockBodyScroll();
|
|
196
|
+
this.headerResizeObserver?.disconnect();
|
|
164
197
|
this.desktopMediaQuery?.removeEventListener('change', this.boundHandleViewportChange);
|
|
165
198
|
this.hoverCapableMediaQuery?.removeEventListener('change', this.boundHandleHoverCapabilityChange);
|
|
166
199
|
this.cancelScheduledDropdownClose();
|
|
167
200
|
}
|
|
201
|
+
syncHeaderHeight() {
|
|
202
|
+
if (this.isMobileMenuActive())
|
|
203
|
+
return;
|
|
204
|
+
const header = this.el.shadowRoot?.querySelector('.header');
|
|
205
|
+
if (!header)
|
|
206
|
+
return;
|
|
207
|
+
this.el.style.setProperty('--weg-header-height', `${header.offsetHeight}px`);
|
|
208
|
+
}
|
|
209
|
+
handleScroll() {
|
|
210
|
+
if (this.scrollTicking)
|
|
211
|
+
return;
|
|
212
|
+
this.scrollTicking = true;
|
|
213
|
+
requestAnimationFrame(() => {
|
|
214
|
+
this.scrollTicking = false;
|
|
215
|
+
this.updateHeaderVisibility();
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
updateHeaderVisibility() {
|
|
219
|
+
if (this.isMobileMenuActive()) {
|
|
220
|
+
if (this.headerHidden)
|
|
221
|
+
this.headerHidden = false;
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const scrollY = window.scrollY;
|
|
225
|
+
const delta = scrollY - this.lastScrollY;
|
|
226
|
+
const threshold = window.innerHeight * HEADER_SCROLL_HIDE_THRESHOLD_VH;
|
|
227
|
+
let nextHidden = this.headerHidden;
|
|
228
|
+
if (scrollY <= threshold) {
|
|
229
|
+
nextHidden = false;
|
|
230
|
+
}
|
|
231
|
+
else if (Math.abs(delta) >= HEADER_SCROLL_DIRECTION_DELTA) {
|
|
232
|
+
nextHidden = delta > 0;
|
|
233
|
+
}
|
|
234
|
+
this.lastScrollY = scrollY;
|
|
235
|
+
if (nextHidden === this.headerHidden)
|
|
236
|
+
return;
|
|
237
|
+
if (nextHidden)
|
|
238
|
+
this.closeDropdown();
|
|
239
|
+
this.headerHidden = nextHidden;
|
|
240
|
+
}
|
|
168
241
|
/**
|
|
169
242
|
* Desktop dropdowns open on hover when viewport is md+ and the device reports hover support.
|
|
170
243
|
* Uses (hover: hover) only — not (pointer: fine), which breaks in DevTools and hybrid devices.
|
|
@@ -182,10 +255,74 @@ export class WegHeader {
|
|
|
182
255
|
}
|
|
183
256
|
handleViewportChange() {
|
|
184
257
|
if (this.desktopMediaQuery?.matches) {
|
|
185
|
-
this.closeMenu();
|
|
258
|
+
this.closeMenu({ announce: false, focusToggle: false });
|
|
186
259
|
}
|
|
187
260
|
this.updateHoverDropdownEnabled();
|
|
188
261
|
}
|
|
262
|
+
announceToScreenReader(message) {
|
|
263
|
+
const region = this.el.shadowRoot?.querySelector('[data-weg-sr-live]');
|
|
264
|
+
if (!region)
|
|
265
|
+
return;
|
|
266
|
+
region.textContent = '';
|
|
267
|
+
requestAnimationFrame(() => {
|
|
268
|
+
region.textContent = message;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
getMenuToggleButton() {
|
|
272
|
+
return this.el.shadowRoot?.querySelector('.menu-toggle');
|
|
273
|
+
}
|
|
274
|
+
getFirstNavFocusable() {
|
|
275
|
+
const panel = this.el.shadowRoot?.querySelector('#weg-header-main-nav');
|
|
276
|
+
if (!panel)
|
|
277
|
+
return null;
|
|
278
|
+
const focusables = panel.querySelectorAll('a[href], button:not([disabled])');
|
|
279
|
+
return focusables[0] ?? null;
|
|
280
|
+
}
|
|
281
|
+
focusFirstNavItem() {
|
|
282
|
+
setTimeout(() => {
|
|
283
|
+
const attemptFocus = (retries = 8) => {
|
|
284
|
+
const target = this.getFirstNavFocusable();
|
|
285
|
+
if (target) {
|
|
286
|
+
target.focus();
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (retries > 0) {
|
|
290
|
+
requestAnimationFrame(() => attemptFocus(retries - 1));
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
attemptFocus();
|
|
294
|
+
}, 0);
|
|
295
|
+
}
|
|
296
|
+
focusMenuToggle() {
|
|
297
|
+
const attemptFocus = (retries = 8) => {
|
|
298
|
+
const toggle = this.getMenuToggleButton();
|
|
299
|
+
if (toggle) {
|
|
300
|
+
toggle.focus();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (retries > 0) {
|
|
304
|
+
requestAnimationFrame(() => attemptFocus(retries - 1));
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
attemptFocus();
|
|
308
|
+
}
|
|
309
|
+
getMenuGroupLinkCount(label) {
|
|
310
|
+
const group = this.getActiveMenu().find((item) => isMenuGroup(item) && item.label === label);
|
|
311
|
+
return group?.items?.length ?? 0;
|
|
312
|
+
}
|
|
313
|
+
formatLinkCount(count) {
|
|
314
|
+
return count === 1 ? '1 link' : `${count} links`;
|
|
315
|
+
}
|
|
316
|
+
formatItemCount(count) {
|
|
317
|
+
return count === 1 ? '1 item' : `${count} items`;
|
|
318
|
+
}
|
|
319
|
+
announceSubmenuExpanded(label) {
|
|
320
|
+
const linkCount = this.getMenuGroupLinkCount(label);
|
|
321
|
+
this.announceToScreenReader(`${label} submenu expanded. ${this.formatLinkCount(linkCount)}.`);
|
|
322
|
+
}
|
|
323
|
+
announceSubmenuCollapsed(label) {
|
|
324
|
+
this.announceToScreenReader(`${label} submenu collapsed.`);
|
|
325
|
+
}
|
|
189
326
|
isMobileMenuActive() {
|
|
190
327
|
return this.menuOpen && !this.desktopMediaQuery?.matches;
|
|
191
328
|
}
|
|
@@ -195,8 +332,39 @@ export class WegHeader {
|
|
|
195
332
|
handleKeyDown(event) {
|
|
196
333
|
if (event.key !== 'Escape')
|
|
197
334
|
return;
|
|
198
|
-
this.
|
|
199
|
-
|
|
335
|
+
if (this.openDropdown) {
|
|
336
|
+
event.preventDefault();
|
|
337
|
+
this.closeDropdownAndFocusTrigger(this.openDropdown);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
if (this.menuOpen) {
|
|
341
|
+
event.preventDefault();
|
|
342
|
+
this.closeMenu({ focusToggle: true });
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
lockBodyScroll() {
|
|
346
|
+
if (this.bodyScrollLocked)
|
|
347
|
+
return;
|
|
348
|
+
this.bodyScrollLockY = window.scrollY;
|
|
349
|
+
document.body.style.overflow = 'hidden';
|
|
350
|
+
document.body.style.position = 'fixed';
|
|
351
|
+
document.body.style.top = `-${this.bodyScrollLockY}px`;
|
|
352
|
+
document.body.style.left = '0';
|
|
353
|
+
document.body.style.right = '0';
|
|
354
|
+
document.body.style.width = '100%';
|
|
355
|
+
this.bodyScrollLocked = true;
|
|
356
|
+
}
|
|
357
|
+
unlockBodyScroll() {
|
|
358
|
+
if (!this.bodyScrollLocked)
|
|
359
|
+
return;
|
|
360
|
+
document.body.style.overflow = '';
|
|
361
|
+
document.body.style.position = '';
|
|
362
|
+
document.body.style.top = '';
|
|
363
|
+
document.body.style.left = '';
|
|
364
|
+
document.body.style.right = '';
|
|
365
|
+
document.body.style.width = '';
|
|
366
|
+
window.scrollTo(0, this.bodyScrollLockY);
|
|
367
|
+
this.bodyScrollLocked = false;
|
|
200
368
|
}
|
|
201
369
|
handleDocumentClick(event) {
|
|
202
370
|
const target = event.target;
|
|
@@ -227,10 +395,12 @@ export class WegHeader {
|
|
|
227
395
|
}
|
|
228
396
|
toggleDropdown(label) {
|
|
229
397
|
if (this.openDropdown === label) {
|
|
398
|
+
this.announceSubmenuCollapsed(label);
|
|
230
399
|
this.closeDropdown();
|
|
231
400
|
return;
|
|
232
401
|
}
|
|
233
402
|
this.openDropdownMenu(label);
|
|
403
|
+
this.announceSubmenuExpanded(label);
|
|
234
404
|
}
|
|
235
405
|
getDropdownPanelId(label) {
|
|
236
406
|
return `weg-header-dropdown-${label.trim().toLowerCase().replace(/\s+/g, '-')}`;
|
|
@@ -283,29 +453,111 @@ export class WegHeader {
|
|
|
283
453
|
if (event.key !== 'ArrowDown')
|
|
284
454
|
return;
|
|
285
455
|
event.preventDefault();
|
|
286
|
-
this.
|
|
456
|
+
if (this.openDropdown !== label) {
|
|
457
|
+
this.openDropdownMenu(label);
|
|
458
|
+
this.announceSubmenuExpanded(label);
|
|
459
|
+
}
|
|
287
460
|
this.focusFirstDropdownLink(itemEl);
|
|
288
461
|
}
|
|
462
|
+
getDropdownLinks(container) {
|
|
463
|
+
return Array.from(container.querySelectorAll('.nav-dropdown__link'));
|
|
464
|
+
}
|
|
465
|
+
focusDropdownLink(container, index) {
|
|
466
|
+
const attemptFocus = (retries = 8) => {
|
|
467
|
+
const links = this.getDropdownLinks(container);
|
|
468
|
+
if (links[index]) {
|
|
469
|
+
links[index].focus();
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (retries > 0) {
|
|
473
|
+
requestAnimationFrame(() => attemptFocus(retries - 1));
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
attemptFocus();
|
|
477
|
+
}
|
|
289
478
|
focusFirstDropdownLink(itemEl) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
479
|
+
this.focusDropdownLink(itemEl, 0);
|
|
480
|
+
}
|
|
481
|
+
getDropdownContainer(label) {
|
|
482
|
+
const panelId = this.getDropdownPanelId(label);
|
|
483
|
+
return this.el.shadowRoot?.querySelector(`#${panelId}-trigger`)?.closest('.nav-dropdown') ?? null;
|
|
484
|
+
}
|
|
485
|
+
closeDropdownAndFocusTrigger(labelOrContainer) {
|
|
486
|
+
const container = typeof labelOrContainer === 'string'
|
|
487
|
+
? this.getDropdownContainer(labelOrContainer)
|
|
488
|
+
: labelOrContainer;
|
|
489
|
+
const trigger = container?.querySelector('.nav-dropdown__trigger');
|
|
490
|
+
const label = typeof labelOrContainer === 'string'
|
|
491
|
+
? labelOrContainer
|
|
492
|
+
: (trigger?.querySelector('.nav-dropdown__label')?.textContent?.trim() ?? '');
|
|
493
|
+
if (label && this.openDropdown === label) {
|
|
494
|
+
this.announceSubmenuCollapsed(label);
|
|
495
|
+
}
|
|
496
|
+
this.closeDropdown();
|
|
497
|
+
const attemptFocus = (retries = 8) => {
|
|
498
|
+
if (trigger && this.el.shadowRoot?.contains(trigger)) {
|
|
499
|
+
trigger.focus();
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (retries > 0) {
|
|
503
|
+
requestAnimationFrame(() => attemptFocus(retries - 1));
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
attemptFocus();
|
|
507
|
+
}
|
|
508
|
+
handleDropdownLinkKeyDown(event, container) {
|
|
509
|
+
const links = this.getDropdownLinks(container);
|
|
510
|
+
const currentIndex = links.indexOf(event.currentTarget);
|
|
511
|
+
if (currentIndex === -1)
|
|
512
|
+
return;
|
|
513
|
+
if (event.key === 'ArrowDown') {
|
|
514
|
+
event.preventDefault();
|
|
515
|
+
links[(currentIndex + 1) % links.length]?.focus();
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
if (event.key === 'ArrowUp') {
|
|
519
|
+
event.preventDefault();
|
|
520
|
+
links[(currentIndex - 1 + links.length) % links.length]?.focus();
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (event.key === 'Escape') {
|
|
524
|
+
event.preventDefault();
|
|
525
|
+
event.stopPropagation();
|
|
526
|
+
this.closeDropdownAndFocusTrigger(container);
|
|
527
|
+
}
|
|
294
528
|
}
|
|
295
529
|
toggleMenu() {
|
|
296
530
|
if (this.menuOpen) {
|
|
297
|
-
this.closeMenu();
|
|
531
|
+
this.closeMenu({ focusToggle: false });
|
|
298
532
|
return;
|
|
299
533
|
}
|
|
300
534
|
this.openMenu();
|
|
301
535
|
}
|
|
302
536
|
openMenu() {
|
|
303
537
|
this.menuOpen = true;
|
|
538
|
+
this.headerHidden = false;
|
|
304
539
|
this.closeDropdown();
|
|
540
|
+
if (!this.desktopMediaQuery?.matches) {
|
|
541
|
+
this.lockBodyScroll();
|
|
542
|
+
const itemCount = this.getActiveMenu().length;
|
|
543
|
+
this.announceToScreenReader(`${MAIN_NAV_LABEL} opened. ${this.formatItemCount(itemCount)}.`);
|
|
544
|
+
this.focusFirstNavItem();
|
|
545
|
+
}
|
|
305
546
|
}
|
|
306
|
-
closeMenu() {
|
|
547
|
+
closeMenu(options = {}) {
|
|
548
|
+
const { announce = true, focusToggle = false } = options;
|
|
549
|
+
const wasMobileActive = this.isMobileMenuActive();
|
|
307
550
|
this.menuOpen = false;
|
|
308
551
|
this.closeDropdown();
|
|
552
|
+
this.unlockBodyScroll();
|
|
553
|
+
this.syncHeaderHeight();
|
|
554
|
+
this.updateHeaderVisibility();
|
|
555
|
+
if (announce && wasMobileActive) {
|
|
556
|
+
this.announceToScreenReader(`${MAIN_NAV_LABEL} closed.`);
|
|
557
|
+
}
|
|
558
|
+
if (focusToggle) {
|
|
559
|
+
this.focusMenuToggle();
|
|
560
|
+
}
|
|
309
561
|
}
|
|
310
562
|
getAccountHome() {
|
|
311
563
|
return normalizeAccountBase(this.accountBaseUrl);
|
|
@@ -334,23 +586,60 @@ export class WegHeader {
|
|
|
334
586
|
const name = this.userName?.trim();
|
|
335
587
|
return name || 'Manage Account';
|
|
336
588
|
}
|
|
337
|
-
|
|
338
|
-
return (
|
|
589
|
+
shouldExcludeFromNavActive(item, accountHome) {
|
|
590
|
+
return isSignOutLink(item) || isSignInLink(item, accountHome);
|
|
339
591
|
}
|
|
340
|
-
|
|
341
|
-
return (
|
|
592
|
+
isNavItemActive(href, mode) {
|
|
593
|
+
return isNavLinkActive(this.currentPath ?? '', href, mode);
|
|
342
594
|
}
|
|
343
|
-
|
|
595
|
+
resolveAriaCurrentHref() {
|
|
596
|
+
const accountHome = this.getAccountHome();
|
|
597
|
+
return resolveAriaCurrentNavKey(this.getActiveMenu(), this.currentPath ?? '', (item) => this.shouldExcludeFromNavActive(item, accountHome));
|
|
598
|
+
}
|
|
599
|
+
isAriaCurrentHref(href, ariaCurrentKey) {
|
|
600
|
+
return isAriaCurrentNavHref(href, ariaCurrentKey);
|
|
601
|
+
}
|
|
602
|
+
renderIconNavLink(link, Icon, className, onNavigate, ariaCurrentHref = null) {
|
|
603
|
+
const active = this.isNavItemActive(link.href, 'prefix');
|
|
604
|
+
const ariaCurrent = this.isAriaCurrentHref(link.href, ariaCurrentHref);
|
|
605
|
+
return (h("li", { class: "main-nav__item main-nav__item--auth", key: link.label }, h("a", { class: {
|
|
606
|
+
'sign-in-link': true,
|
|
607
|
+
[className]: true,
|
|
608
|
+
'nav-link--active': active,
|
|
609
|
+
}, href: link.href, "aria-current": ariaCurrent ? 'page' : undefined, onClick: () => onNavigate?.() }, h(Icon, null), link.label)));
|
|
610
|
+
}
|
|
611
|
+
renderFlatNavLink(link, onNavigate, ariaCurrentHref = null) {
|
|
612
|
+
const active = this.isNavItemActive(link.href, 'prefix');
|
|
613
|
+
const ariaCurrent = this.isAriaCurrentHref(link.href, ariaCurrentHref);
|
|
614
|
+
return (h("li", { class: "main-nav__item", key: link.label }, h("a", { class: {
|
|
615
|
+
'nav-link': true,
|
|
616
|
+
'nav-link--active': active,
|
|
617
|
+
}, href: link.href, "aria-current": ariaCurrent ? 'page' : undefined, onClick: () => onNavigate?.() }, link.label)));
|
|
618
|
+
}
|
|
619
|
+
renderMenuGroup(item, onNavigate, ariaCurrentHref = null) {
|
|
344
620
|
const items = item.items ?? [];
|
|
345
621
|
const isOpen = this.openDropdown === item.label;
|
|
346
622
|
const panelId = this.getDropdownPanelId(item.label);
|
|
623
|
+
const groupActive = items.some((child) => this.isNavItemActive(child.href, 'exact'));
|
|
347
624
|
return (h("li", { class: "main-nav__item main-nav__item--dropdown", key: item.label }, h("div", { class: "nav-dropdown", onMouseEnter: () => this.handleDropdownPointerEnter(item.label), onMouseLeave: (event) => this.handleDropdownPointerLeave(event), onFocusout: (event) => this.handleDropdownFocusOut(event, item.label) }, h("button", { type: "button", class: {
|
|
348
625
|
'nav-dropdown__trigger': true,
|
|
349
626
|
'nav-dropdown__trigger--open': isOpen,
|
|
627
|
+
'nav-dropdown__trigger--active': groupActive,
|
|
350
628
|
}, "aria-expanded": isOpen ? 'true' : 'false', "aria-haspopup": "true", "aria-controls": panelId, id: `${panelId}-trigger`, onClick: (event) => this.handleDropdownTriggerClick(event, item.label), onKeyDown: (event) => {
|
|
351
629
|
const container = event.currentTarget.closest('.nav-dropdown');
|
|
352
630
|
this.handleDropdownTriggerKeyDown(event, item.label, container);
|
|
353
|
-
} }, h("span", { class: "nav-dropdown__label" }, item.label), h(ToggleIcon, { expanded: isOpen })), isOpen ? (h("div", { class: "nav-dropdown__panel", id: panelId, role: "region", "aria-labelledby": `${panelId}-trigger`, onMouseEnter: () => this.handleDropdownPanelPointerEnter(item.label), onMouseLeave: (event) => this.handleDropdownPanelPointerLeave(event) }, h("div", { class: "nav-dropdown__accent" }, h("
|
|
631
|
+
} }, h("span", { class: "nav-dropdown__label" }, item.label), h(ToggleIcon, { expanded: isOpen })), isOpen ? (h("div", { class: "nav-dropdown__panel", id: panelId, role: "region", "aria-labelledby": `${panelId}-trigger`, onMouseEnter: () => this.handleDropdownPanelPointerEnter(item.label), onMouseLeave: (event) => this.handleDropdownPanelPointerLeave(event) }, h("div", { class: "nav-dropdown__accent" }, h("ul", { class: "nav-dropdown__links" }, items.map((child, index) => {
|
|
632
|
+
const childActive = this.isNavItemActive(child.href, 'exact');
|
|
633
|
+
const ariaCurrent = this.isAriaCurrentHref(child.href, ariaCurrentHref);
|
|
634
|
+
const positionLabel = `${child.label}, link ${index + 1} of ${items.length}`;
|
|
635
|
+
return (h("li", { key: `${item.label}:${child.label}` }, h("a", { class: {
|
|
636
|
+
'nav-dropdown__link': true,
|
|
637
|
+
'nav-dropdown__link--active': childActive,
|
|
638
|
+
}, href: child.href, "aria-label": positionLabel, "aria-current": ariaCurrent ? 'page' : undefined, onClick: () => onNavigate?.(), onKeyDown: (event) => {
|
|
639
|
+
const dropdownContainer = event.currentTarget.closest('.nav-dropdown');
|
|
640
|
+
this.handleDropdownLinkKeyDown(event, dropdownContainer);
|
|
641
|
+
} }, child.label)));
|
|
642
|
+
}))))) : null)));
|
|
354
643
|
}
|
|
355
644
|
handleAuthClick(event, action, _href, onNavigate) {
|
|
356
645
|
const emitted = this.wegAuthClick.emit({ action });
|
|
@@ -376,18 +665,22 @@ export class WegHeader {
|
|
|
376
665
|
[options.className || '']: !!options.className,
|
|
377
666
|
}, href: link.href, "aria-label": link.label, onClick: (event) => this.handleAuthClick(event, action, link.href, onNavigate) }, h(Icon, null), !iconOnly ? link.label : null, iconOnly ? h("span", { class: "sr-only" }, link.label) : null));
|
|
378
667
|
}
|
|
379
|
-
renderManageAccountAnchor(onNavigate, iconOnly = false) {
|
|
668
|
+
renderManageAccountAnchor(onNavigate, iconOnly = false, ariaCurrentHref = null) {
|
|
380
669
|
const label = this.getManageAccountLabel();
|
|
670
|
+
const href = accountPath(this.getAccountHome(), '/account/manage');
|
|
671
|
+
const active = this.isNavItemActive(href, 'prefix');
|
|
672
|
+
const ariaCurrent = this.isAriaCurrentHref(href, ariaCurrentHref);
|
|
381
673
|
return (h("a", { class: {
|
|
382
674
|
'sign-in-link': !iconOnly,
|
|
383
675
|
'manage-account-link': true,
|
|
384
676
|
'icon-button': iconOnly,
|
|
385
|
-
|
|
677
|
+
'nav-link--active': active,
|
|
678
|
+
}, href: href, "aria-label": label, "aria-current": ariaCurrent ? 'page' : undefined, onClick: () => onNavigate?.() }, h(SignInIcon, null), !iconOnly ? label : null, iconOnly ? h("span", { class: "sr-only" }, label) : null));
|
|
386
679
|
}
|
|
387
|
-
renderNavItem(item, onNavigate) {
|
|
680
|
+
renderNavItem(item, onNavigate, ariaCurrentHref = null) {
|
|
388
681
|
const accountHome = this.getAccountHome();
|
|
389
682
|
if (isMenuGroup(item)) {
|
|
390
|
-
return this.renderMenuGroup(item, onNavigate);
|
|
683
|
+
return this.renderMenuGroup(item, onNavigate, ariaCurrentHref);
|
|
391
684
|
}
|
|
392
685
|
if (isSignOutLink(item)) {
|
|
393
686
|
return (h("li", { class: "main-nav__item main-nav__item--auth", key: "sign-out" }, this.renderAuthControl({ link: item, action: 'sign-out', onNavigate })));
|
|
@@ -396,18 +689,19 @@ export class WegHeader {
|
|
|
396
689
|
return (h("li", { class: "main-nav__item main-nav__item--auth", key: "sign-in" }, this.renderAuthControl({ link: item, action: 'sign-in', onNavigate })));
|
|
397
690
|
}
|
|
398
691
|
if (isManageAccountLink(item, accountHome)) {
|
|
399
|
-
return (h("li", { class: "main-nav__item main-nav__item--auth", key: "manage-account" }, this.renderManageAccountAnchor(onNavigate)));
|
|
692
|
+
return (h("li", { class: "main-nav__item main-nav__item--auth", key: "manage-account" }, this.renderManageAccountAnchor(onNavigate, false, ariaCurrentHref)));
|
|
400
693
|
}
|
|
401
694
|
if (this.signedIn && isFindJobLink(item)) {
|
|
402
|
-
return this.renderIconNavLink(item, FindJobIcon, 'find-a-job-link', onNavigate);
|
|
695
|
+
return this.renderIconNavLink(item, FindJobIcon, 'find-a-job-link', onNavigate, ariaCurrentHref);
|
|
403
696
|
}
|
|
404
697
|
if (this.signedIn && isDashboardLink(item, accountHome)) {
|
|
405
|
-
return this.renderIconNavLink(item, DashboardIcon, 'dashboard-link', onNavigate);
|
|
698
|
+
return this.renderIconNavLink(item, DashboardIcon, 'dashboard-link', onNavigate, ariaCurrentHref);
|
|
406
699
|
}
|
|
407
|
-
return this.renderFlatNavLink(item, onNavigate);
|
|
700
|
+
return this.renderFlatNavLink(item, onNavigate, ariaCurrentHref);
|
|
408
701
|
}
|
|
409
702
|
renderNav(onNavigate) {
|
|
410
|
-
|
|
703
|
+
const ariaCurrentHref = this.resolveAriaCurrentHref();
|
|
704
|
+
return (h("nav", { class: "main-nav", "aria-label": MAIN_NAV_LABEL }, h("ul", { class: "main-nav__list" }, this.getActiveMenu().map((item) => this.renderNavItem(item, onNavigate, ariaCurrentHref)))));
|
|
411
705
|
}
|
|
412
706
|
renderCompactAuth(onNavigate) {
|
|
413
707
|
if (this.signedIn) {
|
|
@@ -428,10 +722,12 @@ export class WegHeader {
|
|
|
428
722
|
const logoSrc = this.getLogoSrc();
|
|
429
723
|
const closeMenu = () => this.closeMenu();
|
|
430
724
|
const mobileMenuActive = this.isMobileMenuActive();
|
|
431
|
-
return (h("header", { key: '
|
|
725
|
+
return (h("header", { key: '429d9af0996fa37484ebeea36f1e071fe0ba0789', class: {
|
|
432
726
|
header: true,
|
|
727
|
+
'header--fixed': !mobileMenuActive,
|
|
728
|
+
'header--hidden': this.headerHidden && !mobileMenuActive,
|
|
433
729
|
'header--menu-open': mobileMenuActive,
|
|
434
|
-
} }, h("div", { key: '
|
|
730
|
+
} }, h("div", { key: '2c088a3e602ac2c8751d21376872536e35a0ab7d', class: "header-inner" }, h("div", { key: '91089bfc45b2b3274df00b032cad0adaba79b937', class: "sr-only", "aria-live": "polite", "aria-atomic": "true", "data-weg-sr-live": true }), h("button", { key: 'a66e0f0564014f5041a9fa80b39f74ce177f7125', type: "button", class: "menu-toggle icon-button", "aria-label": mobileMenuActive ? CLOSE_MAIN_NAV_LABEL : OPEN_MAIN_NAV_LABEL, "aria-expanded": mobileMenuActive ? 'true' : 'false', "aria-controls": "weg-header-main-nav", onClick: () => this.toggleMenu() }, mobileMenuActive ? h(CloseIcon, null) : h(HamburgerIcon, null)), h(Logo, { key: '917d134565c899e9a25557bafd62bf8b0de75e32', href: logoHref, src: logoSrc }), h("div", { key: '7b6bcd11271e00ddf0ca7d34bc1c8d2ad8342ed4', class: "header-actions" }, this.renderCompactAuth(mobileMenuActive ? closeMenu : undefined)), h("div", { key: '1a6779e99be4704b6f9093501dd230a891b81c17', class: "main-nav-panel", id: "weg-header-main-nav", role: mobileMenuActive ? 'dialog' : undefined, "aria-modal": mobileMenuActive ? 'true' : undefined, "aria-label": mobileMenuActive ? MAIN_NAV_LABEL : undefined }, this.renderNav(mobileMenuActive ? closeMenu : undefined)))));
|
|
435
731
|
}
|
|
436
732
|
static get is() { return "weg-header"; }
|
|
437
733
|
static get encapsulation() { return "shadow"; }
|
|
@@ -466,7 +762,7 @@ export class WegHeader {
|
|
|
466
762
|
"optional": true,
|
|
467
763
|
"docs": {
|
|
468
764
|
"tags": [],
|
|
469
|
-
"text": "Layout payload, supplied by the host application.\n\nExpected shape:\n```json\n{\n \"header\": {\n \"menu\": [\n { \"label\": \"Find a job\", \"items\": [{ \"label\": \"Graduates\", \"href\": \"...\" }] },\n { \"label\": \"Career advice\", \"href\": \"/career-advice\" },\n { \"label\": \"Sign in\", \"href\": \"/account/login\" }\n ]\n }\n}\n```"
|
|
765
|
+
"text": "Layout payload, supplied by the host application.\r\n\r\nExpected shape:\r\n```json\r\n{\r\n \"header\": {\r\n \"menu\": [\r\n { \"label\": \"Find a job\", \"items\": [{ \"label\": \"Graduates\", \"href\": \"...\" }] },\r\n { \"label\": \"Career advice\", \"href\": \"/career-advice\" },\r\n { \"label\": \"Sign in\", \"href\": \"/account/login\" }\r\n ]\r\n }\r\n}\r\n```"
|
|
470
766
|
},
|
|
471
767
|
"getter": false,
|
|
472
768
|
"setter": false,
|
|
@@ -524,12 +820,31 @@ export class WegHeader {
|
|
|
524
820
|
"optional": true,
|
|
525
821
|
"docs": {
|
|
526
822
|
"tags": [],
|
|
527
|
-
"text": "Account portal origin for sign-in, register, dashboard, and manage-account links.\nDefaults to production when omitted."
|
|
823
|
+
"text": "Account portal origin for sign-in, register, dashboard, and manage-account links.\r\nDefaults to production when omitted."
|
|
528
824
|
},
|
|
529
825
|
"getter": false,
|
|
530
826
|
"setter": false,
|
|
531
827
|
"reflect": false,
|
|
532
828
|
"attribute": "account-base-url"
|
|
829
|
+
},
|
|
830
|
+
"currentPath": {
|
|
831
|
+
"type": "string",
|
|
832
|
+
"mutable": false,
|
|
833
|
+
"complexType": {
|
|
834
|
+
"original": "string",
|
|
835
|
+
"resolved": "string",
|
|
836
|
+
"references": {}
|
|
837
|
+
},
|
|
838
|
+
"required": false,
|
|
839
|
+
"optional": true,
|
|
840
|
+
"docs": {
|
|
841
|
+
"tags": [],
|
|
842
|
+
"text": "Current page path from the host application (e.g. `/career-advice/my-article`).\r\nInclude a query string when matching dropdown links that use search params."
|
|
843
|
+
},
|
|
844
|
+
"getter": false,
|
|
845
|
+
"setter": false,
|
|
846
|
+
"reflect": false,
|
|
847
|
+
"attribute": "current-path"
|
|
533
848
|
}
|
|
534
849
|
};
|
|
535
850
|
}
|
|
@@ -537,7 +852,8 @@ export class WegHeader {
|
|
|
537
852
|
return {
|
|
538
853
|
"resolved": {},
|
|
539
854
|
"menuOpen": {},
|
|
540
|
-
"openDropdown": {}
|
|
855
|
+
"openDropdown": {},
|
|
856
|
+
"headerHidden": {}
|
|
541
857
|
};
|
|
542
858
|
}
|
|
543
859
|
static get events() {
|
|
@@ -549,7 +865,7 @@ export class WegHeader {
|
|
|
549
865
|
"composed": true,
|
|
550
866
|
"docs": {
|
|
551
867
|
"tags": [],
|
|
552
|
-
"text": "Fired when the user clicks Sign in or Sign out.\nCall `event.preventDefault()` in the host to handle navigation/logout yourself."
|
|
868
|
+
"text": "Fired when the user clicks Sign in or Sign out.\r\nCall `event.preventDefault()` in the host to handle navigation/logout yourself."
|
|
553
869
|
},
|
|
554
870
|
"complexType": {
|
|
555
871
|
"original": "{ action: LayoutHeaderAuthAction }",
|