@hortonstudio/main 1.2.35 → 1.4.1

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.
@@ -3,26 +3,38 @@ let supportsInert = null;
3
3
  let screenReaderLiveRegion = null;
4
4
 
5
5
  export const init = () => {
6
+ // Ensure DOM is ready before initializing
7
+ if (document.readyState === "loading") {
8
+ document.addEventListener("DOMContentLoaded", initializeNavbar);
9
+ } else {
10
+ initializeNavbar();
11
+ }
12
+ return { result: "navbar initialized" };
13
+ };
14
+
15
+ function initializeNavbar() {
6
16
  setupAccessibilityFeatures();
7
17
  setupDynamicDropdowns();
8
18
  setupMobileMenuButton();
9
19
  setupMobileMenuARIA();
10
20
  setupMobileMenuBreakpointHandler();
11
- return { result: 'navbar initialized' };
12
- };
21
+ }
13
22
 
14
23
  // Accessibility features setup
15
24
  function setupAccessibilityFeatures() {
16
25
  // Check inert support once
17
- supportsInert = 'inert' in HTMLElement.prototype;
18
-
19
- // Create screen reader live region
20
- screenReaderLiveRegion = document.createElement('div');
21
- screenReaderLiveRegion.setAttribute('aria-live', 'polite');
22
- screenReaderLiveRegion.setAttribute('aria-atomic', 'true');
23
- screenReaderLiveRegion.className = 'u-sr-only';
24
- screenReaderLiveRegion.style.cssText = 'position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;';
25
- document.body.appendChild(screenReaderLiveRegion);
26
+ supportsInert = "inert" in HTMLElement.prototype;
27
+
28
+ // Create screen reader live region only if body exists
29
+ if (document.body) {
30
+ screenReaderLiveRegion = document.createElement("div");
31
+ screenReaderLiveRegion.setAttribute("aria-live", "polite");
32
+ screenReaderLiveRegion.setAttribute("aria-atomic", "true");
33
+ screenReaderLiveRegion.className = "u-sr-only";
34
+ screenReaderLiveRegion.style.cssText =
35
+ "position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;";
36
+ document.body.appendChild(screenReaderLiveRegion);
37
+ }
26
38
  }
27
39
 
28
40
  // Inert polyfill for browsers that don't support it
@@ -31,29 +43,30 @@ function setElementInert(element, isInert) {
31
43
  element.inert = isInert;
32
44
  } else {
33
45
  // Polyfill: manage tabindex for all focusable elements
34
- const focusableSelectors = 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])';
46
+ const focusableSelectors =
47
+ 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])';
35
48
  const focusableElements = element.querySelectorAll(focusableSelectors);
36
-
49
+
37
50
  if (isInert) {
38
51
  // Store original tabindex values and disable
39
- focusableElements.forEach(el => {
40
- const currentTabindex = el.getAttribute('tabindex');
41
- el.setAttribute('data-inert-tabindex', currentTabindex || '0');
42
- el.setAttribute('tabindex', '-1');
52
+ focusableElements.forEach((el) => {
53
+ const currentTabindex = el.getAttribute("tabindex");
54
+ el.setAttribute("data-inert-tabindex", currentTabindex || "0");
55
+ el.setAttribute("tabindex", "-1");
43
56
  });
44
- element.setAttribute('data-inert', 'true');
57
+ element.setAttribute("data-inert", "true");
45
58
  } else {
46
59
  // Restore original tabindex values
47
- focusableElements.forEach(el => {
48
- const originalTabindex = el.getAttribute('data-inert-tabindex');
49
- if (originalTabindex === '0') {
50
- el.removeAttribute('tabindex');
60
+ focusableElements.forEach((el) => {
61
+ const originalTabindex = el.getAttribute("data-inert-tabindex");
62
+ if (originalTabindex === "0") {
63
+ el.removeAttribute("tabindex");
51
64
  } else if (originalTabindex) {
52
- el.setAttribute('tabindex', originalTabindex);
65
+ el.setAttribute("tabindex", originalTabindex);
53
66
  }
54
- el.removeAttribute('data-inert-tabindex');
67
+ el.removeAttribute("data-inert-tabindex");
55
68
  });
56
- element.removeAttribute('data-inert');
69
+ element.removeAttribute("data-inert");
57
70
  }
58
71
  }
59
72
  }
@@ -62,97 +75,103 @@ function setElementInert(element, isInert) {
62
75
  function announceToScreenReader(message) {
63
76
  if (screenReaderLiveRegion) {
64
77
  screenReaderLiveRegion.textContent = message;
65
-
78
+
66
79
  // Clear after a delay to allow for repeat announcements
67
80
  setTimeout(() => {
68
- screenReaderLiveRegion.textContent = '';
81
+ screenReaderLiveRegion.textContent = "";
69
82
  }, 1000);
70
83
  }
71
84
  }
72
85
 
73
86
  // Extract menu name from element text or aria-label
74
87
  function getMenuName(element) {
75
- const text = element.textContent?.trim() ||
76
- element.getAttribute('aria-label') ||
77
- 'menu';
78
- return text.replace(/^(Open|Close)\s+/i, '').replace(/\s+(menu|navigation)$/i, '');
88
+ const text =
89
+ (element.textContent && element.textContent.trim()) ||
90
+ element.getAttribute("aria-label") ||
91
+ "menu";
92
+ return text
93
+ .replace(/^(Open|Close)\s+/i, "")
94
+ .replace(/\s+(menu|navigation)$/i, "");
79
95
  }
80
96
 
81
97
  // Desktop dropdown system
82
98
  function setupDynamicDropdowns() {
83
- const dropdownWrappers = document.querySelectorAll('[data-hs-nav="dropdown"]');
99
+ const dropdownWrappers = document.querySelectorAll(
100
+ '[data-hs-nav="dropdown"]',
101
+ );
84
102
  const allDropdowns = [];
85
-
103
+
86
104
  const closeAllDropdowns = (exceptWrapper = null) => {
87
- allDropdowns.forEach(dropdown => {
105
+ allDropdowns.forEach((dropdown) => {
88
106
  if (dropdown.wrapper !== exceptWrapper && dropdown.isOpen) {
89
107
  dropdown.closeDropdown();
90
108
  }
91
109
  });
92
110
  };
93
-
94
- dropdownWrappers.forEach(wrapper => {
95
- const toggle = wrapper.querySelector('a');
111
+
112
+ dropdownWrappers.forEach((wrapper) => {
113
+ const toggle = wrapper.querySelector("a");
96
114
  if (!toggle) return;
97
-
98
- const allElements = wrapper.querySelectorAll('*');
115
+
116
+ const allElements = wrapper.querySelectorAll("*");
99
117
  let dropdownList = null;
100
-
118
+
101
119
  for (const element of allElements) {
102
- const links = element.querySelectorAll('a');
120
+ const links = element.querySelectorAll("a");
103
121
  if (links.length >= 2 && !element.contains(toggle)) {
104
122
  dropdownList = element;
105
123
  break;
106
124
  }
107
125
  }
108
-
126
+
109
127
  if (!dropdownList) return;
110
-
111
- const toggleText = toggle.textContent?.trim() || 'dropdown';
128
+
129
+ const toggleText =
130
+ (toggle.textContent && toggle.textContent.trim()) || "dropdown";
112
131
  const sanitizedText = sanitizeForID(toggleText);
113
132
  const toggleId = `navbar-dropdown-${sanitizedText}-toggle`;
114
133
  const listId = `navbar-dropdown-${sanitizedText}-list`;
115
-
134
+
116
135
  toggle.id = toggleId;
117
- toggle.setAttribute('aria-haspopup', 'menu');
118
- toggle.setAttribute('aria-expanded', 'false');
119
- toggle.setAttribute('aria-controls', listId);
120
-
136
+ toggle.setAttribute("aria-haspopup", "menu");
137
+ toggle.setAttribute("aria-expanded", "false");
138
+ toggle.setAttribute("aria-controls", listId);
139
+
121
140
  dropdownList.id = listId;
122
- dropdownList.setAttribute('role', 'menu');
123
- dropdownList.setAttribute('aria-hidden', 'true');
124
-
125
- const menuItems = dropdownList.querySelectorAll('a');
126
- menuItems.forEach(item => {
127
- item.setAttribute('role', 'menuitem');
128
- item.setAttribute('tabindex', '-1');
141
+ dropdownList.setAttribute("role", "menu");
142
+ dropdownList.setAttribute("aria-hidden", "true");
143
+
144
+ const menuItems = dropdownList.querySelectorAll("a");
145
+ menuItems.forEach((item) => {
146
+ item.setAttribute("role", "menuitem");
147
+ item.setAttribute("tabindex", "-1");
129
148
  });
130
-
149
+
131
150
  let isOpen = false;
132
151
  let currentMenuItemIndex = -1;
133
-
152
+
134
153
  function openDropdown() {
135
154
  if (isOpen) return;
136
155
  closeAllDropdowns(wrapper);
137
156
  isOpen = true;
138
- toggle.setAttribute('aria-expanded', 'true');
139
- dropdownList.setAttribute('aria-hidden', 'false');
140
- menuItems.forEach(item => {
141
- item.setAttribute('tabindex', '0');
157
+ toggle.setAttribute("aria-expanded", "true");
158
+ dropdownList.setAttribute("aria-hidden", "false");
159
+ menuItems.forEach((item) => {
160
+ item.setAttribute("tabindex", "0");
142
161
  });
143
-
162
+
144
163
  // Announce to screen readers
145
164
  const menuName = getMenuName(toggle);
146
165
  announceToScreenReader(`${menuName} menu opened`);
147
-
148
- const clickEvent = new MouseEvent('click', {
166
+
167
+ const clickEvent = new MouseEvent("click", {
149
168
  bubbles: true,
150
169
  cancelable: true,
151
- view: window
170
+ view: window,
152
171
  });
153
172
  wrapper.dispatchEvent(clickEvent);
154
173
  }
155
-
174
+
156
175
  function closeDropdown() {
157
176
  if (!isOpen) return;
158
177
  const shouldRestoreFocus = dropdownList.contains(document.activeElement);
@@ -161,76 +180,80 @@ function setupDynamicDropdowns() {
161
180
  if (shouldRestoreFocus) {
162
181
  toggle.focus();
163
182
  }
164
- toggle.setAttribute('aria-expanded', 'false');
165
- dropdownList.setAttribute('aria-hidden', 'true');
166
- menuItems.forEach(item => {
167
- item.setAttribute('tabindex', '-1');
183
+ toggle.setAttribute("aria-expanded", "false");
184
+ dropdownList.setAttribute("aria-hidden", "true");
185
+ menuItems.forEach((item) => {
186
+ item.setAttribute("tabindex", "-1");
168
187
  });
169
-
188
+
170
189
  // Announce to screen readers
171
190
  const menuName = getMenuName(toggle);
172
191
  announceToScreenReader(`${menuName} menu closed`);
173
-
174
- const clickEvent = new MouseEvent('click', {
192
+
193
+ const clickEvent = new MouseEvent("click", {
175
194
  bubbles: true,
176
195
  cancelable: true,
177
- view: window
196
+ view: window,
178
197
  });
179
198
  wrapper.dispatchEvent(clickEvent);
180
199
  }
181
-
182
- wrapper.addEventListener('mouseenter', () => {
200
+
201
+ wrapper.addEventListener("mouseenter", () => {
183
202
  if (!isOpen) {
184
- const clickEvent = new MouseEvent('click', {
203
+ const clickEvent = new MouseEvent("click", {
185
204
  bubbles: true,
186
205
  cancelable: true,
187
- view: window
206
+ view: window,
188
207
  });
189
208
  wrapper.dispatchEvent(clickEvent);
190
209
  closeAllDropdowns(wrapper);
191
210
  isOpen = true;
192
- toggle.setAttribute('aria-expanded', 'true');
193
- dropdownList.setAttribute('aria-hidden', 'false');
194
- menuItems.forEach(item => {
195
- item.setAttribute('tabindex', '0');
211
+ toggle.setAttribute("aria-expanded", "true");
212
+ dropdownList.setAttribute("aria-hidden", "false");
213
+ menuItems.forEach((item) => {
214
+ item.setAttribute("tabindex", "0");
196
215
  });
197
216
  }
198
217
  });
199
-
200
- wrapper.addEventListener('mouseleave', () => {
218
+
219
+ wrapper.addEventListener("mouseleave", () => {
201
220
  if (isOpen) {
202
221
  if (dropdownList.contains(document.activeElement)) {
203
222
  toggle.focus();
204
223
  }
205
- const clickEvent = new MouseEvent('click', {
224
+ const clickEvent = new MouseEvent("click", {
206
225
  bubbles: true,
207
226
  cancelable: true,
208
- view: window
227
+ view: window,
209
228
  });
210
229
  wrapper.dispatchEvent(clickEvent);
211
230
  isOpen = false;
212
- toggle.setAttribute('aria-expanded', 'false');
213
- dropdownList.setAttribute('aria-hidden', 'true');
214
- menuItems.forEach(item => {
215
- item.setAttribute('tabindex', '-1');
231
+ toggle.setAttribute("aria-expanded", "false");
232
+ dropdownList.setAttribute("aria-hidden", "true");
233
+ menuItems.forEach((item) => {
234
+ item.setAttribute("tabindex", "-1");
216
235
  });
217
236
  currentMenuItemIndex = -1;
218
237
  }
219
238
  });
220
-
221
- document.addEventListener('keydown', function(e) {
239
+
240
+ document.addEventListener("keydown", function (e) {
222
241
  if (!isOpen) return;
223
242
  if (!wrapper.contains(document.activeElement)) return;
224
-
225
- if (e.key === 'ArrowDown') {
243
+
244
+ if (e.key === "ArrowDown") {
226
245
  e.preventDefault();
227
246
  if (document.activeElement === toggle) {
228
247
  currentMenuItemIndex = 0;
229
248
  menuItems[currentMenuItemIndex].focus();
230
249
  } else {
231
250
  if (currentMenuItemIndex === menuItems.length - 1) {
232
- const nextElement = wrapper.nextElementSibling?.querySelector('a, button') ||
233
- document.querySelector('.navbar_cartsearch_wrap a, .navbar_cartsearch_wrap button');
251
+ const nextElement =
252
+ (wrapper.nextElementSibling &&
253
+ wrapper.nextElementSibling.querySelector("a, button")) ||
254
+ document.querySelector(
255
+ ".navbar_cartsearch_wrap a, .navbar_cartsearch_wrap button",
256
+ );
234
257
  if (nextElement) {
235
258
  closeDropdown();
236
259
  nextElement.focus();
@@ -240,14 +263,16 @@ function setupDynamicDropdowns() {
240
263
  currentMenuItemIndex = (currentMenuItemIndex + 1) % menuItems.length;
241
264
  menuItems[currentMenuItemIndex].focus();
242
265
  }
243
- } else if (e.key === 'ArrowUp') {
266
+ } else if (e.key === "ArrowUp") {
244
267
  e.preventDefault();
245
268
  if (document.activeElement === toggle) {
246
269
  currentMenuItemIndex = menuItems.length - 1;
247
270
  menuItems[currentMenuItemIndex].focus();
248
271
  } else {
249
272
  if (currentMenuItemIndex === 0) {
250
- const prevElement = wrapper.previousElementSibling?.querySelector('a, button');
273
+ const prevElement =
274
+ wrapper.previousElementSibling &&
275
+ wrapper.previousElementSibling.querySelector("a, button");
251
276
  if (prevElement) {
252
277
  closeDropdown();
253
278
  prevElement.focus();
@@ -258,10 +283,13 @@ function setupDynamicDropdowns() {
258
283
  return;
259
284
  }
260
285
  }
261
- currentMenuItemIndex = currentMenuItemIndex <= 0 ? menuItems.length - 1 : currentMenuItemIndex - 1;
286
+ currentMenuItemIndex =
287
+ currentMenuItemIndex <= 0
288
+ ? menuItems.length - 1
289
+ : currentMenuItemIndex - 1;
262
290
  menuItems[currentMenuItemIndex].focus();
263
291
  }
264
- } else if (e.key === 'Tab') {
292
+ } else if (e.key === "Tab") {
265
293
  if (e.shiftKey) {
266
294
  if (document.activeElement === menuItems[0]) {
267
295
  e.preventDefault();
@@ -271,8 +299,12 @@ function setupDynamicDropdowns() {
271
299
  } else {
272
300
  if (document.activeElement === menuItems[menuItems.length - 1]) {
273
301
  e.preventDefault();
274
- const nextElement = wrapper.nextElementSibling?.querySelector('a, button') ||
275
- document.querySelector('.navbar_cartsearch_wrap a, .navbar_cartsearch_wrap button');
302
+ const nextElement =
303
+ (wrapper.nextElementSibling &&
304
+ wrapper.nextElementSibling.querySelector("a, button")) ||
305
+ document.querySelector(
306
+ ".navbar_cartsearch_wrap a, .navbar_cartsearch_wrap button",
307
+ );
276
308
  closeDropdown();
277
309
  if (nextElement) {
278
310
  setTimeout(() => {
@@ -281,32 +313,32 @@ function setupDynamicDropdowns() {
281
313
  }
282
314
  }
283
315
  }
284
- } else if (e.key === 'Escape') {
316
+ } else if (e.key === "Escape") {
285
317
  e.preventDefault();
286
318
  closeDropdown();
287
319
  toggle.focus();
288
- } else if (e.key === 'Home') {
320
+ } else if (e.key === "Home") {
289
321
  e.preventDefault();
290
322
  currentMenuItemIndex = 0;
291
323
  menuItems[0].focus();
292
- } else if (e.key === 'End') {
324
+ } else if (e.key === "End") {
293
325
  e.preventDefault();
294
326
  currentMenuItemIndex = menuItems.length - 1;
295
327
  menuItems[menuItems.length - 1].focus();
296
- } else if (e.key === ' ') {
328
+ } else if (e.key === " ") {
297
329
  e.preventDefault();
298
330
  }
299
331
  });
300
-
301
- toggle.addEventListener('keydown', function(e) {
302
- if (e.key === 'ArrowDown') {
332
+
333
+ toggle.addEventListener("keydown", function (e) {
334
+ if (e.key === "ArrowDown") {
303
335
  e.preventDefault();
304
336
  openDropdown();
305
337
  if (menuItems.length > 0) {
306
338
  currentMenuItemIndex = 0;
307
339
  setTimeout(() => menuItems[0].focus(), 100);
308
340
  }
309
- } else if (e.key === ' ') {
341
+ } else if (e.key === " ") {
310
342
  e.preventDefault();
311
343
  if (isOpen) {
312
344
  closeDropdown();
@@ -317,7 +349,7 @@ function setupDynamicDropdowns() {
317
349
  setTimeout(() => menuItems[0].focus(), 100);
318
350
  }
319
351
  }
320
- } else if (e.key === 'ArrowUp') {
352
+ } else if (e.key === "ArrowUp") {
321
353
  e.preventDefault();
322
354
  if (isOpen) {
323
355
  currentMenuItemIndex = menuItems.length - 1;
@@ -325,29 +357,39 @@ function setupDynamicDropdowns() {
325
357
  } else {
326
358
  closeDropdown();
327
359
  }
328
- } else if (e.key === 'Escape') {
360
+ } else if (e.key === "Escape") {
329
361
  e.preventDefault();
330
362
  closeDropdown();
331
363
  }
332
364
  });
333
-
334
- document.addEventListener('click', function(e) {
365
+
366
+ toggle.addEventListener("click", function(e) {
367
+ if (e.isTrusted) {
368
+ // This is a real user click - prevent it
369
+ e.preventDefault();
370
+ e.stopPropagation();
371
+ return false;
372
+ }
373
+ // Programmatic clicks (from hover/keyboard) proceed normally
374
+ });
375
+
376
+ document.addEventListener("click", function (e) {
335
377
  if (!wrapper.contains(e.target) && isOpen) {
336
378
  closeDropdown();
337
379
  }
338
380
  });
339
-
381
+
340
382
  allDropdowns.push({
341
383
  wrapper,
342
384
  isOpen: () => isOpen,
343
385
  closeDropdown,
344
386
  toggle,
345
- dropdownList
387
+ dropdownList,
346
388
  });
347
389
  });
348
-
349
- document.addEventListener('focusin', function(e) {
350
- allDropdowns.forEach(dropdown => {
390
+
391
+ document.addEventListener("focusin", function (e) {
392
+ allDropdowns.forEach((dropdown) => {
351
393
  if (dropdown.isOpen() && !dropdown.wrapper.contains(e.target)) {
352
394
  dropdown.closeDropdown();
353
395
  }
@@ -359,66 +401,73 @@ function setupDynamicDropdowns() {
359
401
 
360
402
  // Desktop left/right arrow navigation
361
403
  function addDesktopArrowNavigation() {
362
- document.addEventListener('keydown', function(e) {
363
- if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return;
364
-
404
+ document.addEventListener("keydown", function (e) {
405
+ if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
406
+
365
407
  const mobileMenu = document.querySelector('[data-hs-nav="menu"]');
366
408
  if (mobileMenu && mobileMenu.contains(document.activeElement)) return;
367
-
368
- const navbar = document.querySelector('[data-hs-nav="wrapper"]') ||
369
- document.querySelector('.navbar_component') ||
370
- document.querySelector('nav[role="navigation"]') ||
371
- document.querySelector('nav');
372
-
409
+
410
+ const navbar =
411
+ document.querySelector('[data-hs-nav="wrapper"]') ||
412
+ document.querySelector(".navbar_component") ||
413
+ document.querySelector('nav[role="navigation"]') ||
414
+ document.querySelector("nav");
415
+
373
416
  if (!navbar || !navbar.contains(document.activeElement)) return;
374
-
375
- const openDropdownList = navbar.querySelector('[aria-hidden="false"][role="menu"]');
376
- if (openDropdownList && openDropdownList.contains(document.activeElement)) return;
377
-
417
+
418
+ const openDropdownList = navbar.querySelector(
419
+ '[aria-hidden="false"][role="menu"]',
420
+ );
421
+ if (openDropdownList && openDropdownList.contains(document.activeElement))
422
+ return;
423
+
378
424
  e.preventDefault();
379
-
380
- const allNavbarElements = navbar.querySelectorAll('a, button');
381
- const focusableElements = Array.from(allNavbarElements).filter(el => {
382
- if (el.getAttribute('tabindex') === '-1') return false;
383
-
425
+
426
+ const allNavbarElements = navbar.querySelectorAll("a, button");
427
+ const focusableElements = Array.from(allNavbarElements).filter((el) => {
428
+ if (el.getAttribute("tabindex") === "-1") return false;
429
+
384
430
  const isInDropdownList = el.closest('[role="menu"]');
385
431
  if (isInDropdownList) return false;
386
-
432
+
387
433
  const isInMobileMenu = el.closest('[data-hs-nav="menu"]');
388
434
  if (isInMobileMenu) return false;
389
-
435
+
390
436
  const computedStyle = window.getComputedStyle(el);
391
- const isHidden = computedStyle.display === 'none' ||
392
- computedStyle.visibility === 'hidden' ||
393
- computedStyle.opacity === '0' ||
394
- el.offsetWidth === 0 ||
395
- el.offsetHeight === 0;
437
+ const isHidden =
438
+ computedStyle.display === "none" ||
439
+ computedStyle.visibility === "hidden" ||
440
+ computedStyle.opacity === "0" ||
441
+ el.offsetWidth === 0 ||
442
+ el.offsetHeight === 0;
396
443
  if (isHidden) return false;
397
-
444
+
398
445
  let parent = el.parentElement;
399
446
  while (parent && parent !== navbar) {
400
447
  const parentStyle = window.getComputedStyle(parent);
401
- const parentHidden = parentStyle.display === 'none' ||
402
- parentStyle.visibility === 'hidden' ||
403
- parent.offsetWidth === 0 ||
404
- parent.offsetHeight === 0;
448
+ const parentHidden =
449
+ parentStyle.display === "none" ||
450
+ parentStyle.visibility === "hidden" ||
451
+ parent.offsetWidth === 0 ||
452
+ parent.offsetHeight === 0;
405
453
  if (parentHidden) return false;
406
454
  parent = parent.parentElement;
407
455
  }
408
-
456
+
409
457
  return true;
410
458
  });
411
-
459
+
412
460
  const currentIndex = focusableElements.indexOf(document.activeElement);
413
461
  if (currentIndex === -1) return;
414
-
462
+
415
463
  let nextIndex;
416
- if (e.key === 'ArrowRight') {
464
+ if (e.key === "ArrowRight") {
417
465
  nextIndex = (currentIndex + 1) % focusableElements.length;
418
466
  } else {
419
- nextIndex = currentIndex === 0 ? focusableElements.length - 1 : currentIndex - 1;
467
+ nextIndex =
468
+ currentIndex === 0 ? focusableElements.length - 1 : currentIndex - 1;
420
469
  }
421
-
470
+
422
471
  focusableElements[nextIndex].focus();
423
472
  });
424
473
  }
@@ -427,241 +476,287 @@ function addDesktopArrowNavigation() {
427
476
  function setupMobileMenuButton() {
428
477
  const menuButton = document.querySelector('[data-hs-nav="menubtn"]');
429
478
  const mobileMenu = document.querySelector('[data-hs-nav="menu"]');
430
-
479
+
431
480
  if (!menuButton || !mobileMenu) return;
432
-
481
+
433
482
  const menuId = `mobile-menu-${Date.now()}`;
434
-
435
- menuButton.setAttribute('aria-expanded', 'false');
436
- menuButton.setAttribute('aria-controls', menuId);
437
- menuButton.setAttribute('aria-label', 'Open navigation menu');
438
-
483
+
484
+ menuButton.setAttribute("aria-expanded", "false");
485
+ menuButton.setAttribute("aria-controls", menuId);
486
+ menuButton.setAttribute("aria-label", "Open navigation menu");
487
+
439
488
  mobileMenu.id = menuId;
440
- mobileMenu.setAttribute('role', 'dialog');
441
- mobileMenu.setAttribute('aria-modal', 'true');
489
+ mobileMenu.setAttribute("role", "dialog");
490
+ mobileMenu.setAttribute("aria-modal", "true");
442
491
  setElementInert(mobileMenu, true);
443
-
492
+
444
493
  let isMenuOpen = false;
445
-
494
+
446
495
  function shouldPreventMobileMenu() {
447
- const menuHideElement = document.querySelector('.menu-hide.is-mobile');
496
+ const menuHideElement = document.querySelector(".menu-hide.is-mobile");
448
497
  if (!menuHideElement) return false;
449
-
498
+
450
499
  const computedStyle = window.getComputedStyle(menuHideElement);
451
- return computedStyle.display === 'none';
500
+ return computedStyle.display === "none";
452
501
  }
453
-
502
+
454
503
  function openMenu() {
455
504
  if (isMenuOpen || shouldPreventMobileMenu()) return;
456
505
  isMenuOpen = true;
457
-
458
- // Add body overflow hidden class
459
- document.body.classList.add('u-overflow-hidden');
460
-
461
- // Add blur effect to modal blur elements
462
- document.querySelectorAll('[data-hs-nav="modal-blur"]').forEach(element => {
463
- element.style.display = 'block';
464
- element.style.opacity = '0.5';
465
- element.style.transition = 'opacity 0.3s ease';
466
- });
467
-
468
- menuButton.setAttribute('aria-expanded', 'true');
469
- menuButton.setAttribute('aria-label', 'Close navigation menu');
470
- setElementInert(mobileMenu, false);
471
-
472
- // Announce to screen readers
473
- const menuName = getMenuName(menuButton);
474
- announceToScreenReader(`${menuName} opened`);
475
-
476
- // Prevent tabbing outside navbar using tabindex management
477
- const navbarWrapper = document.querySelector('[data-hs-nav="wrapper"]') ||
478
- document.querySelector('.navbar_component') ||
479
- document.querySelector('nav[role="navigation"]') ||
480
- document.querySelector('nav');
481
-
482
- const allFocusableElements = document.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
483
- allFocusableElements.forEach(el => {
484
- if (navbarWrapper && !navbarWrapper.contains(el)) {
485
- el.setAttribute('data-mobile-menu-tabindex', el.getAttribute('tabindex') || '0');
486
- el.setAttribute('tabindex', '-1');
487
- }
488
- });
489
-
490
- const clickEvent = new MouseEvent('click', {
491
- bubbles: true,
492
- cancelable: true,
493
- view: window
494
- });
495
- menuButton.dispatchEvent(clickEvent);
506
+
507
+ try {
508
+ // Add body overflow hidden class
509
+ document.body.classList.add("u-overflow-hidden");
510
+
511
+ // Add blur effect to modal blur elements
512
+ document
513
+ .querySelectorAll('[data-hs-nav="modal-blur"]')
514
+ .forEach((element) => {
515
+ element.style.display = "block";
516
+ element.style.opacity = "0.5";
517
+ element.style.transition = "opacity 0.3s ease";
518
+ });
519
+
520
+ menuButton.setAttribute("aria-expanded", "true");
521
+ menuButton.setAttribute("aria-label", "Close navigation menu");
522
+ setElementInert(mobileMenu, false);
523
+
524
+ // Announce to screen readers
525
+ const menuName = getMenuName(menuButton);
526
+ announceToScreenReader(`${menuName} opened`);
527
+
528
+ // Prevent tabbing outside navbar using tabindex management
529
+ const navbarWrapper =
530
+ document.querySelector('[data-hs-nav="wrapper"]') ||
531
+ document.querySelector(".navbar_component") ||
532
+ document.querySelector('nav[role="navigation"]') ||
533
+ document.querySelector("nav");
534
+
535
+ const allFocusableElements = document.querySelectorAll(
536
+ 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])',
537
+ );
538
+ allFocusableElements.forEach((el) => {
539
+ if (navbarWrapper && !navbarWrapper.contains(el)) {
540
+ el.setAttribute(
541
+ "data-mobile-menu-tabindex",
542
+ el.getAttribute("tabindex") || "0",
543
+ );
544
+ el.setAttribute("tabindex", "-1");
545
+ }
546
+ });
547
+
548
+ const clickEvent = new MouseEvent("click", {
549
+ bubbles: true,
550
+ cancelable: true,
551
+ view: window,
552
+ });
553
+ menuButton.dispatchEvent(clickEvent);
554
+ } catch (e) {
555
+ // DOM operation failed, restore state
556
+ isMenuOpen = false;
557
+ menuButton.setAttribute("aria-expanded", "false");
558
+ menuButton.setAttribute("aria-label", "Open navigation menu");
559
+ }
496
560
  }
497
-
561
+
498
562
  function closeMenu() {
499
563
  if (!isMenuOpen) return;
500
564
  isMenuOpen = false;
501
-
502
- // Remove body overflow hidden class
503
- document.body.classList.remove('u-overflow-hidden');
504
-
505
- // Remove blur effect from modal blur elements
506
- document.querySelectorAll('[data-hs-nav="modal-blur"]').forEach(element => {
507
- element.style.opacity = '0';
508
- element.style.transition = 'opacity 0.3s ease';
509
- setTimeout(() => {
510
- element.style.display = 'none';
511
- }, 300);
512
- });
513
-
514
- if (mobileMenu.contains(document.activeElement)) {
515
- menuButton.focus();
516
- }
517
- menuButton.setAttribute('aria-expanded', 'false');
518
- menuButton.setAttribute('aria-label', 'Open navigation menu');
519
- setElementInert(mobileMenu, true);
520
-
521
- // Announce to screen readers
522
- const menuName = getMenuName(menuButton);
523
- announceToScreenReader(`${menuName} closed`);
524
-
525
- // Restore tabbing to entire page using tabindex management
526
- const elementsToRestore = document.querySelectorAll('[data-mobile-menu-tabindex]');
527
- elementsToRestore.forEach(el => {
528
- const originalTabindex = el.getAttribute('data-mobile-menu-tabindex');
529
- if (originalTabindex === '0') {
530
- el.removeAttribute('tabindex');
531
- } else {
532
- el.setAttribute('tabindex', originalTabindex);
565
+
566
+ try {
567
+ // Remove body overflow hidden class
568
+ document.body.classList.remove("u-overflow-hidden");
569
+
570
+ // Remove blur effect from modal blur elements
571
+ document
572
+ .querySelectorAll('[data-hs-nav="modal-blur"]')
573
+ .forEach((element) => {
574
+ element.style.opacity = "0";
575
+ element.style.transition = "opacity 0.3s ease";
576
+ setTimeout(() => {
577
+ element.style.display = "none";
578
+ }, 300);
579
+ });
580
+
581
+ if (mobileMenu.contains(document.activeElement)) {
582
+ menuButton.focus();
533
583
  }
534
- el.removeAttribute('data-mobile-menu-tabindex');
535
- });
536
-
537
- const clickEvent = new MouseEvent('click', {
538
- bubbles: true,
539
- cancelable: true,
540
- view: window
541
- });
542
- menuButton.dispatchEvent(clickEvent);
584
+ menuButton.setAttribute("aria-expanded", "false");
585
+ menuButton.setAttribute("aria-label", "Open navigation menu");
586
+ setElementInert(mobileMenu, true);
587
+
588
+ // Announce to screen readers
589
+ const menuName = getMenuName(menuButton);
590
+ announceToScreenReader(`${menuName} closed`);
591
+
592
+ // Restore tabbing to entire page using tabindex management
593
+ const elementsToRestore = document.querySelectorAll(
594
+ "[data-mobile-menu-tabindex]",
595
+ );
596
+ elementsToRestore.forEach((el) => {
597
+ const originalTabindex = el.getAttribute("data-mobile-menu-tabindex");
598
+ if (originalTabindex === "0") {
599
+ el.removeAttribute("tabindex");
600
+ } else {
601
+ el.setAttribute("tabindex", originalTabindex);
602
+ }
603
+ el.removeAttribute("data-mobile-menu-tabindex");
604
+ });
605
+
606
+ const clickEvent = new MouseEvent("click", {
607
+ bubbles: true,
608
+ cancelable: true,
609
+ view: window,
610
+ });
611
+ menuButton.dispatchEvent(clickEvent);
612
+ } catch (e) {
613
+ // DOM operation failed, ensure consistent state
614
+ isMenuOpen = false;
615
+ menuButton.setAttribute("aria-expanded", "false");
616
+ menuButton.setAttribute("aria-label", "Open navigation menu");
617
+ }
543
618
  }
544
-
619
+
545
620
  function toggleMenu() {
546
621
  if (shouldPreventMobileMenu()) return;
547
-
622
+
548
623
  if (isMenuOpen) {
549
624
  closeMenu();
550
625
  } else {
551
626
  openMenu();
552
627
  }
553
628
  }
554
-
555
- menuButton.addEventListener('keydown', function(e) {
556
- if (e.key === 'Enter' || e.key === ' ') {
629
+
630
+ menuButton.addEventListener("keydown", function (e) {
631
+ if (e.key === "Enter" || e.key === " ") {
557
632
  e.preventDefault();
558
633
  toggleMenu();
559
- } else if (e.key === 'ArrowDown') {
634
+ } else if (e.key === "ArrowDown") {
560
635
  e.preventDefault();
561
636
  if (!isMenuOpen) {
562
637
  openMenu();
563
638
  }
564
- const firstElement = mobileMenu.querySelector('button, a');
639
+ const firstElement = mobileMenu.querySelector("button, a");
565
640
  if (firstElement) {
566
641
  firstElement.focus();
567
642
  }
568
- } else if (e.key === 'ArrowUp') {
643
+ } else if (e.key === "ArrowUp") {
569
644
  e.preventDefault();
570
645
  if (isMenuOpen) {
571
646
  closeMenu();
572
647
  }
573
648
  }
574
649
  });
575
-
576
- menuButton.addEventListener('click', function(e) {
650
+
651
+ menuButton.addEventListener("click", function (e) {
577
652
  if (!e.isTrusted) return;
578
-
653
+
579
654
  if (shouldPreventMobileMenu()) return;
580
-
655
+
581
656
  if (isMenuOpen && mobileMenu.contains(document.activeElement)) {
582
657
  menuButton.focus();
583
658
  }
584
-
659
+
585
660
  const newMenuState = !isMenuOpen;
586
661
  isMenuOpen = newMenuState;
587
-
662
+
588
663
  // Handle body overflow class
589
664
  if (isMenuOpen) {
590
- document.body.classList.add('u-overflow-hidden');
665
+ document.body.classList.add("u-overflow-hidden");
591
666
  } else {
592
- document.body.classList.remove('u-overflow-hidden');
667
+ document.body.classList.remove("u-overflow-hidden");
593
668
  }
594
-
669
+
595
670
  // Handle blur effect
596
- document.querySelectorAll('[data-hs-nav="modal-blur"]').forEach(element => {
597
- if (isMenuOpen) {
598
- element.style.display = 'block';
599
- element.style.opacity = '0.5';
600
- element.style.transition = 'opacity 0.3s ease';
601
- } else {
602
- element.style.opacity = '0';
603
- element.style.transition = 'opacity 0.3s ease';
604
- setTimeout(() => {
605
- element.style.display = 'none';
606
- }, 300);
607
- }
608
- });
609
-
610
- menuButton.setAttribute('aria-expanded', isMenuOpen);
611
- menuButton.setAttribute('aria-label',
612
- isMenuOpen ? 'Close navigation menu' : 'Open navigation menu'
671
+ document
672
+ .querySelectorAll('[data-hs-nav="modal-blur"]')
673
+ .forEach((element) => {
674
+ if (isMenuOpen) {
675
+ element.style.display = "block";
676
+ element.style.opacity = "0.5";
677
+ element.style.transition = "opacity 0.3s ease";
678
+ } else {
679
+ element.style.opacity = "0";
680
+ element.style.transition = "opacity 0.3s ease";
681
+ setTimeout(() => {
682
+ element.style.display = "none";
683
+ }, 300);
684
+ }
685
+ });
686
+
687
+ menuButton.setAttribute("aria-expanded", isMenuOpen);
688
+ menuButton.setAttribute(
689
+ "aria-label",
690
+ isMenuOpen ? "Close navigation menu" : "Open navigation menu",
613
691
  );
614
692
  setElementInert(mobileMenu, !isMenuOpen);
615
-
693
+
616
694
  // Handle tabindex management for external clicks
617
695
  if (isMenuOpen) {
618
- const navbarWrapper = document.querySelector('[data-hs-nav="wrapper"]') ||
619
- document.querySelector('.navbar_component') ||
620
- document.querySelector('nav[role="navigation"]') ||
621
- document.querySelector('nav');
622
-
623
- const allFocusableElements = document.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
624
- allFocusableElements.forEach(el => {
696
+ const navbarWrapper =
697
+ document.querySelector('[data-hs-nav="wrapper"]') ||
698
+ document.querySelector(".navbar_component") ||
699
+ document.querySelector('nav[role="navigation"]') ||
700
+ document.querySelector("nav");
701
+
702
+ const allFocusableElements = document.querySelectorAll(
703
+ 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])',
704
+ );
705
+ allFocusableElements.forEach((el) => {
625
706
  if (navbarWrapper && !navbarWrapper.contains(el)) {
626
- el.setAttribute('data-mobile-menu-tabindex', el.getAttribute('tabindex') || '0');
627
- el.setAttribute('tabindex', '-1');
707
+ el.setAttribute(
708
+ "data-mobile-menu-tabindex",
709
+ el.getAttribute("tabindex") || "0",
710
+ );
711
+ el.setAttribute("tabindex", "-1");
628
712
  }
629
713
  });
630
714
  } else {
631
- const elementsToRestore = document.querySelectorAll('[data-mobile-menu-tabindex]');
632
- elementsToRestore.forEach(el => {
633
- const originalTabindex = el.getAttribute('data-mobile-menu-tabindex');
634
- if (originalTabindex === '0') {
635
- el.removeAttribute('tabindex');
715
+ const elementsToRestore = document.querySelectorAll(
716
+ "[data-mobile-menu-tabindex]",
717
+ );
718
+ elementsToRestore.forEach((el) => {
719
+ const originalTabindex = el.getAttribute("data-mobile-menu-tabindex");
720
+ if (originalTabindex === "0") {
721
+ el.removeAttribute("tabindex");
636
722
  } else {
637
- el.setAttribute('tabindex', originalTabindex);
723
+ el.setAttribute("tabindex", originalTabindex);
638
724
  }
639
- el.removeAttribute('data-mobile-menu-tabindex');
725
+ el.removeAttribute("data-mobile-menu-tabindex");
640
726
  });
641
727
  }
642
728
  });
643
-
729
+
644
730
  // Store the menu state and functions for breakpoint handler
645
731
  window.mobileMenuState = {
646
732
  isMenuOpen: () => isMenuOpen,
647
733
  closeMenu: closeMenu,
648
- openMenu: openMenu
734
+ openMenu: openMenu,
649
735
  };
736
+
737
+ // Cleanup function for window.mobileMenuState
738
+ if (typeof window !== "undefined") {
739
+ window.addEventListener("pagehide", () => {
740
+ if (window.mobileMenuState) {
741
+ delete window.mobileMenuState;
742
+ }
743
+ });
744
+ }
650
745
  }
651
746
 
652
747
  // Mobile menu breakpoint handler
653
748
  function setupMobileMenuBreakpointHandler() {
654
749
  let preventedMenuState = false;
655
-
750
+
656
751
  function handleBreakpointChange() {
657
- const menuHideElement = document.querySelector('.menu-hide.is-mobile');
752
+ const menuHideElement = document.querySelector(".menu-hide.is-mobile");
658
753
  if (!menuHideElement) return;
659
-
754
+
660
755
  const computedStyle = window.getComputedStyle(menuHideElement);
661
- const shouldPrevent = computedStyle.display === 'none';
662
-
756
+ const shouldPrevent = computedStyle.display === "none";
757
+
663
758
  if (!window.mobileMenuState) return;
664
-
759
+
665
760
  if (shouldPrevent && window.mobileMenuState.isMenuOpen()) {
666
761
  // Store that the menu was open before being prevented
667
762
  preventedMenuState = true;
@@ -672,19 +767,24 @@ function setupMobileMenuBreakpointHandler() {
672
767
  window.mobileMenuState.openMenu();
673
768
  }
674
769
  }
675
-
770
+
676
771
  // Use ResizeObserver for more accurate detection
677
- if (window.ResizeObserver) {
678
- const resizeObserver = new ResizeObserver(handleBreakpointChange);
679
- const menuHideElement = document.querySelector('.menu-hide.is-mobile');
680
- if (menuHideElement) {
681
- resizeObserver.observe(menuHideElement);
772
+ if (typeof ResizeObserver !== "undefined") {
773
+ try {
774
+ const resizeObserver = new ResizeObserver(handleBreakpointChange);
775
+ const menuHideElement = document.querySelector(".menu-hide.is-mobile");
776
+ if (menuHideElement) {
777
+ resizeObserver.observe(menuHideElement);
778
+ }
779
+ } catch (e) {
780
+ // ResizeObserver not supported or error occurred
781
+ // Silently fall back to resize event
682
782
  }
683
783
  }
684
-
784
+
685
785
  // Fallback to resize event
686
- window.addEventListener('resize', handleBreakpointChange);
687
-
786
+ window.addEventListener("resize", handleBreakpointChange);
787
+
688
788
  // Initial check
689
789
  handleBreakpointChange();
690
790
  }
@@ -692,9 +792,9 @@ function setupMobileMenuBreakpointHandler() {
692
792
  function sanitizeForID(text) {
693
793
  return text
694
794
  .toLowerCase()
695
- .replace(/[^a-z0-9\s]/g, '')
696
- .replace(/\s+/g, '-')
697
- .replace(/^-+|-+$/g, '')
795
+ .replace(/[^a-z0-9\s]/g, "")
796
+ .replace(/\s+/g, "-")
797
+ .replace(/^-+|-+$/g, "")
698
798
  .substring(0, 50);
699
799
  }
700
800
 
@@ -703,11 +803,11 @@ function setupMobileMenuARIA() {
703
803
  const menuContainer = document.querySelector('[data-hs-nav="menu"]');
704
804
  if (!menuContainer) return;
705
805
 
706
- const buttons = menuContainer.querySelectorAll('button');
707
- const links = menuContainer.querySelectorAll('a');
806
+ const buttons = menuContainer.querySelectorAll("button");
807
+ const links = menuContainer.querySelectorAll("a");
708
808
 
709
- buttons.forEach(button => {
710
- const buttonText = button.textContent?.trim();
809
+ buttons.forEach((button) => {
810
+ const buttonText = button.textContent && button.textContent.trim();
711
811
  if (!buttonText) return;
712
812
 
713
813
  const sanitizedText = sanitizeForID(buttonText);
@@ -715,66 +815,77 @@ function setupMobileMenuARIA() {
715
815
  const listId = `navbar-mobile-${sanitizedText}-list`;
716
816
 
717
817
  button.id = buttonId;
718
- button.setAttribute('aria-expanded', 'false');
719
- button.setAttribute('aria-controls', listId);
818
+ button.setAttribute("aria-expanded", "false");
819
+ button.setAttribute("aria-controls", listId);
720
820
 
721
821
  // Look for dropdown list in the same container as the button
722
822
  let dropdownList = null;
723
- const buttonContainer = button.closest('.menu-card_dropdown, .menu_contain, [data-hs-nav="menu"]');
724
-
823
+ const buttonContainer = button.closest(
824
+ '.menu-card_dropdown, .menu_contain, [data-hs-nav="menu"]',
825
+ );
826
+
725
827
  if (buttonContainer) {
726
828
  // First try to find a list element within the same container
727
- dropdownList = buttonContainer.querySelector('.menu-card_list, .dropdown-list, [role="menu"]');
728
-
829
+ dropdownList = buttonContainer.querySelector(
830
+ '.menu-card_list, .dropdown-list, [role="menu"]',
831
+ );
832
+
729
833
  // If not found, look for any element with multiple links that's not the button itself
730
- if (!dropdownList || !dropdownList.querySelector('a')) {
731
- const allListElements = buttonContainer.querySelectorAll('div, ul, nav');
732
- dropdownList = Array.from(allListElements).find(el =>
733
- el.querySelectorAll('a').length > 1 &&
734
- !el.contains(button) &&
735
- el !== button
834
+ if (!dropdownList || !dropdownList.querySelector("a")) {
835
+ const allListElements =
836
+ buttonContainer.querySelectorAll("div, ul, nav");
837
+ dropdownList = Array.from(allListElements).find(
838
+ (el) =>
839
+ el.querySelectorAll("a").length > 1 &&
840
+ !el.contains(button) &&
841
+ el !== button,
736
842
  );
737
843
  }
738
844
  }
739
845
 
740
- if (dropdownList && dropdownList.querySelector('a')) {
846
+ if (dropdownList && dropdownList.querySelector("a")) {
741
847
  dropdownList.id = listId;
742
848
  setElementInert(dropdownList, true);
743
849
 
744
- button.addEventListener('click', function() {
745
- const isExpanded = button.getAttribute('aria-expanded') === 'true';
850
+ button.addEventListener("click", function () {
851
+ const isExpanded = button.getAttribute("aria-expanded") === "true";
746
852
  const newState = !isExpanded;
747
- button.setAttribute('aria-expanded', newState);
853
+ button.setAttribute("aria-expanded", newState);
748
854
  setElementInert(dropdownList, !newState);
749
-
855
+
750
856
  // Announce to screen readers
751
857
  const menuName = getMenuName(button);
752
- announceToScreenReader(`${menuName} submenu ${newState ? 'opened' : 'closed'}`);
858
+ announceToScreenReader(
859
+ `${menuName} submenu ${newState ? "opened" : "closed"}`,
860
+ );
753
861
  });
754
862
  }
755
863
  });
756
864
 
757
- links.forEach(link => {
758
- const linkText = link.textContent?.trim();
865
+ links.forEach((link) => {
866
+ const linkText = link.textContent && link.textContent.trim();
759
867
  if (!linkText) return;
760
868
 
761
869
  const sanitizedText = sanitizeForID(linkText);
762
870
  const linkId = `navbar-mobile-${sanitizedText}-link`;
763
871
  link.id = linkId;
764
872
  });
765
-
873
+
766
874
  setupMobileMenuArrowNavigation(menuContainer);
767
875
  }
768
876
 
769
877
  // Mobile menu arrow navigation
770
878
  function setupMobileMenuArrowNavigation(menuContainer) {
771
879
  function getFocusableElements() {
772
- const allElements = menuContainer.querySelectorAll('button, a');
773
- return Array.from(allElements).filter(el => {
880
+ const allElements = menuContainer.querySelectorAll("button, a");
881
+ return Array.from(allElements).filter((el) => {
774
882
  let current = el;
775
883
  while (current && current !== menuContainer) {
776
884
  // Check both native inert and polyfill inert
777
- if (current.inert === true || current.getAttribute('data-inert') === 'true') {
885
+ if (
886
+ current.inert === true ||
887
+ current.getAttribute("data-inert") === "true"
888
+ ) {
778
889
  return false;
779
890
  }
780
891
  current = current.parentElement;
@@ -782,17 +893,17 @@ function setupMobileMenuArrowNavigation(menuContainer) {
782
893
  return true;
783
894
  });
784
895
  }
785
-
896
+
786
897
  let currentFocusIndex = -1;
787
-
788
- menuContainer.addEventListener('keydown', function(e) {
898
+
899
+ menuContainer.addEventListener("keydown", function (e) {
789
900
  const focusableElements = getFocusableElements();
790
901
  if (focusableElements.length === 0) return;
791
-
902
+
792
903
  const activeElement = document.activeElement;
793
904
  currentFocusIndex = focusableElements.indexOf(activeElement);
794
-
795
- if (e.key === 'ArrowDown') {
905
+
906
+ if (e.key === "ArrowDown") {
796
907
  e.preventDefault();
797
908
  if (currentFocusIndex >= focusableElements.length - 1) {
798
909
  currentFocusIndex = 0;
@@ -800,10 +911,12 @@ function setupMobileMenuArrowNavigation(menuContainer) {
800
911
  currentFocusIndex = currentFocusIndex + 1;
801
912
  }
802
913
  focusableElements[currentFocusIndex].focus();
803
- } else if (e.key === 'ArrowUp') {
914
+ } else if (e.key === "ArrowUp") {
804
915
  e.preventDefault();
805
916
  if (currentFocusIndex <= 0) {
806
- const mobileMenuButton = document.querySelector('[data-hs-nav="menubtn"]');
917
+ const mobileMenuButton = document.querySelector(
918
+ '[data-hs-nav="menubtn"]',
919
+ );
807
920
  if (mobileMenuButton) {
808
921
  mobileMenuButton.focus();
809
922
  return;
@@ -811,40 +924,50 @@ function setupMobileMenuArrowNavigation(menuContainer) {
811
924
  }
812
925
  currentFocusIndex = currentFocusIndex - 1;
813
926
  focusableElements[currentFocusIndex].focus();
814
- } else if (e.key === 'ArrowRight') {
927
+ } else if (e.key === "ArrowRight") {
815
928
  e.preventDefault();
816
- if (activeElement.tagName === 'BUTTON' && activeElement.hasAttribute('aria-controls')) {
817
- const isExpanded = activeElement.getAttribute('aria-expanded') === 'true';
929
+ if (
930
+ activeElement.tagName === "BUTTON" &&
931
+ activeElement.hasAttribute("aria-controls")
932
+ ) {
933
+ const isExpanded =
934
+ activeElement.getAttribute("aria-expanded") === "true";
818
935
  if (!isExpanded) {
819
936
  activeElement.click();
820
937
  }
821
938
  return;
822
939
  }
823
- } else if (e.key === 'ArrowLeft') {
940
+ } else if (e.key === "ArrowLeft") {
824
941
  e.preventDefault();
825
- if (activeElement.tagName === 'BUTTON' && activeElement.hasAttribute('aria-controls')) {
826
- const isExpanded = activeElement.getAttribute('aria-expanded') === 'true';
942
+ if (
943
+ activeElement.tagName === "BUTTON" &&
944
+ activeElement.hasAttribute("aria-controls")
945
+ ) {
946
+ const isExpanded =
947
+ activeElement.getAttribute("aria-expanded") === "true";
827
948
  if (isExpanded) {
828
949
  activeElement.click();
829
950
  }
830
951
  return;
831
952
  }
832
- } else if (e.key === 'Home') {
953
+ } else if (e.key === "Home") {
833
954
  e.preventDefault();
834
955
  currentFocusIndex = 0;
835
956
  focusableElements[0].focus();
836
- } else if (e.key === 'End') {
957
+ } else if (e.key === "End") {
837
958
  e.preventDefault();
838
959
  currentFocusIndex = focusableElements.length - 1;
839
960
  focusableElements[focusableElements.length - 1].focus();
840
- } else if (e.key === ' ' && activeElement.tagName === 'A') {
961
+ } else if (e.key === " " && activeElement.tagName === "A") {
841
962
  e.preventDefault();
842
- } else if (e.key === 'Escape') {
843
- const mobileMenuButton = document.querySelector('[data-hs-nav="menubtn"]');
963
+ } else if (e.key === "Escape") {
964
+ const mobileMenuButton = document.querySelector(
965
+ '[data-hs-nav="menubtn"]',
966
+ );
844
967
  if (mobileMenuButton) {
845
968
  mobileMenuButton.click();
846
969
  mobileMenuButton.focus();
847
970
  }
848
971
  }
849
972
  });
850
- }
973
+ }