@hortonstudio/main 1.9.11 → 1.9.20
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/.prettierrc +8 -0
- package/README.md +146 -0
- package/eslint.config.js +32 -0
- package/index.ts +275 -0
- package/package.json +19 -2
- package/public/bootstrap.js +16 -0
- package/src/animations/animations.ts +93 -0
- package/src/animations/functions/counter/counter.ts +137 -0
- package/src/config.json +570 -0
- package/src/config.ts +105 -0
- package/src/modules/default/README.md +167 -0
- package/src/modules/default/default.ts +71 -0
- package/{autoInit → src/modules/default/functions}/accessibility/README.md +44 -12
- package/src/modules/default/functions/accessibility/accessibility.ts +54 -0
- package/src/modules/default/functions/accordion/README.md +451 -0
- package/src/modules/default/functions/accordion/accordion.ts +189 -0
- package/src/modules/default/functions/comparison/comparison.ts +424 -0
- package/src/modules/default/functions/marquee/marquee.ts +206 -0
- package/src/modules/default/functions/navbar/README.md +393 -0
- package/src/modules/default/functions/navbar/functions/arrow-navigation/arrow-navigation.ts +183 -0
- package/src/modules/default/functions/navbar/functions/dropdown/dropdown.ts +313 -0
- package/src/modules/default/functions/navbar/functions/menu/menu.ts +315 -0
- package/src/modules/default/functions/navbar/navbar.ts +51 -0
- package/{autoInit → src/modules/default/functions}/smooth-scroll/README.md +45 -14
- package/{autoInit/smooth-scroll/smooth-scroll.js → src/modules/default/functions/smooth-scroll/smooth-scroll.ts} +33 -38
- package/{autoInit → src/modules/default/functions}/transition/README.md +59 -32
- package/src/modules/default/functions/transition/transition.ts +290 -0
- package/src/modules/normalize/README.md +172 -0
- package/src/modules/normalize/functions/clickable/README.md +84 -0
- package/src/modules/normalize/functions/clickable/clickable.ts +43 -0
- package/src/modules/normalize/functions/clickable/functions/normalize/README.md +213 -0
- package/src/modules/normalize/functions/clickable/functions/normalize/normalize.ts +68 -0
- package/src/modules/normalize/functions/dupe/README.md +405 -0
- package/src/modules/normalize/functions/dupe/dupe.ts +197 -0
- package/src/modules/normalize/functions/sync/sync.ts +378 -0
- package/src/modules/normalize/normalize.ts +58 -0
- package/src/modules/structure/README.md +190 -0
- package/src/modules/structure/functions/form/README.md +94 -0
- package/src/modules/structure/functions/form/form.ts +54 -0
- package/src/modules/structure/functions/form/functions/honeypot/README.md +77 -0
- package/src/modules/structure/functions/form/functions/honeypot/honeypot.ts +37 -0
- package/src/modules/structure/functions/form/functions/range/README.md +410 -0
- package/src/modules/structure/functions/form/functions/range/range.ts +92 -0
- package/src/modules/structure/functions/form/functions/select/README.md +393 -0
- package/src/modules/structure/functions/form/functions/select/functions/custom-select/custom-select.ts +637 -0
- package/src/modules/structure/functions/form/functions/select/functions/states/states.ts +118 -0
- package/src/modules/structure/functions/form/functions/select/select.ts +48 -0
- package/src/modules/structure/functions/form/functions/test/test.ts +132 -0
- package/{autoInit/accessibility → src/modules/structure}/functions/pagination/README.md +147 -72
- package/{autoInit/accessibility/functions/pagination/pagination.js → src/modules/structure/functions/pagination/pagination.ts} +98 -50
- package/{autoInit → src/modules/structure/functions}/site-settings/README.md +57 -27
- package/{autoInit/site-settings/site-settings.js → src/modules/structure/functions/site-settings/site-settings.ts} +36 -32
- package/{autoInit/accessibility → src/modules/structure}/functions/toc/README.md +18 -15
- package/{autoInit/accessibility/functions/toc/toc.js → src/modules/structure/functions/toc/functions/heading-links/heading-links.ts} +43 -63
- package/src/modules/structure/functions/toc/functions/progress-bar/progress-bar.ts +101 -0
- package/src/modules/structure/functions/toc/toc.ts +35 -0
- package/{autoInit/accessibility → src/modules/structure}/functions/year-replacement/README.md +7 -6
- package/src/modules/structure/functions/year-replacement/year-replacement.ts +59 -0
- package/src/modules/structure/structure.ts +59 -0
- package/src/utils/attributeSelector.ts +78 -0
- package/src/utils/cssVariables.ts +24 -0
- package/src/utils/gsap.ts +198 -0
- package/src/utils/heightAnimator.ts +130 -0
- package/src/utils/modalManager.ts +150 -0
- package/src/utils.ts +54 -0
- package/tsconfig.json +24 -0
- package/vite.config.js +45 -0
- package/.claude/settings.local.json +0 -70
- package/archive/hero.js +0 -794
- package/archive/modal.js +0 -80
- package/archive/text.js +0 -628
- package/autoInit/accessibility/accessibility.js +0 -53
- package/autoInit/accessibility/functions/blog-remover/README.md +0 -61
- package/autoInit/accessibility/functions/blog-remover/blog-remover.js +0 -31
- package/autoInit/accessibility/functions/click-forwarding/README.md +0 -60
- package/autoInit/accessibility/functions/click-forwarding/click-forwarding.js +0 -82
- package/autoInit/accessibility/functions/dropdown/README.md +0 -212
- package/autoInit/accessibility/functions/dropdown/dropdown.js +0 -167
- package/autoInit/accessibility/functions/list-accessibility/README.md +0 -56
- package/autoInit/accessibility/functions/list-accessibility/list-accessibility.js +0 -23
- package/autoInit/accessibility/functions/text-synchronization/README.md +0 -62
- package/autoInit/accessibility/functions/text-synchronization/text-synchronization.js +0 -101
- package/autoInit/accessibility/functions/year-replacement/year-replacement.js +0 -43
- package/autoInit/button/README.md +0 -122
- package/autoInit/button/button.js +0 -51
- package/autoInit/counter/README.md +0 -274
- package/autoInit/counter/counter.js +0 -185
- package/autoInit/form/README.md +0 -338
- package/autoInit/form/form.js +0 -374
- package/autoInit/navbar/README.md +0 -366
- package/autoInit/navbar/navbar.js +0 -786
- package/autoInit/transition/transition.js +0 -116
- package/index.js +0 -305
- package/utils/before-after/README.md +0 -520
- package/utils/before-after/before-after.js +0 -653
- package/utils/css-animations/buttons/main/bgbasic/btn-main-bgbasic.html +0 -10
- package/utils/css-animations/buttons/main/bgfill/btn-main-bgfill.html +0 -29
- package/utils/css-animations/buttons/navbar/bgbasic/navbar-main-bgbasic.html +0 -17
- package/utils/css-animations/buttons/navbar/bgbasic/navbar-menu-bgbasic.html +0 -16
- package/utils/css-animations/buttons/navbar/bgfill/navbar-main-bgfill.html +0 -46
- package/utils/css-animations/buttons/navbar/bgfill/navbar-menu-bgfill.html +0 -39
- package/utils/css-animations/buttons/navbar/color/navbar-announce-color.html +0 -5
- package/utils/css-animations/buttons/navbar/color/navbar-main-color.html +0 -7
- package/utils/css-animations/buttons/navbar/color/navbar-menu-color.html +0 -7
- package/utils/css-animations/buttons/navbar/double-slide/navbar-announce-double-slide.html +0 -40
- package/utils/css-animations/buttons/navbar/double-slide/navbar-main-double-slide.html +0 -77
- package/utils/css-animations/buttons/navbar/scale/navbar-announce-scale.html +0 -6
- package/utils/css-animations/buttons/navbar/scale/navbar-main-scale.html +0 -9
- package/utils/css-animations/buttons/navbar/scale/navbar-menu-scale.html +0 -8
- package/utils/css-animations/buttons/navbar/underline/navbar-announce-underline.html +0 -32
- package/utils/css-animations/buttons/navbar/underline/navbar-main-underline.html +0 -56
- package/utils/css-animations/buttons/text/color/text-footer-color.html +0 -5
- package/utils/css-animations/buttons/text/color/text-main-color.html +0 -5
- package/utils/css-animations/buttons/text/double-slide/text-main-double-slide.html +0 -56
- package/utils/css-animations/buttons/text/scale/text-footer-scale.html +0 -6
- package/utils/css-animations/buttons/text/scale/text-main-scale.html +0 -6
- package/utils/css-animations/buttons/text/underline/text-footer-underline.html +0 -45
- package/utils/css-animations/buttons/text/underline/text-main-underline.html +0 -58
- package/utils/css-animations/cards/card-clickable.html +0 -11
- package/utils/css-animations/defaults.html +0 -69
|
@@ -1,786 +0,0 @@
|
|
|
1
|
-
export const init = () => {
|
|
2
|
-
// Centralized cleanup tracking
|
|
3
|
-
const cleanup = {
|
|
4
|
-
observers: [],
|
|
5
|
-
handlers: [],
|
|
6
|
-
state: {
|
|
7
|
-
focusTrapHandler: null,
|
|
8
|
-
dropdownKeydownHandler: null,
|
|
9
|
-
menuKeydownHandler: null
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const addObserver = (observer) => cleanup.observers.push(observer);
|
|
14
|
-
const addHandler = (element, event, handler, options) => {
|
|
15
|
-
element.addEventListener(event, handler, options);
|
|
16
|
-
cleanup.handlers.push({ element, event, handler, options });
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
setupDynamicDropdowns(addObserver, addHandler);
|
|
20
|
-
setupMenuButton(addHandler, addObserver);
|
|
21
|
-
setupMenuARIA(addHandler, addObserver);
|
|
22
|
-
setupMenuDisplayObserver(addObserver);
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
result: "navbar initialized",
|
|
26
|
-
destroy: () => {
|
|
27
|
-
// Disconnect all observers
|
|
28
|
-
cleanup.observers.forEach(obs => obs.disconnect());
|
|
29
|
-
cleanup.observers.length = 0;
|
|
30
|
-
|
|
31
|
-
// Remove all event listeners
|
|
32
|
-
cleanup.handlers.forEach(({ element, event, handler, options }) => {
|
|
33
|
-
element.removeEventListener(event, handler, options);
|
|
34
|
-
});
|
|
35
|
-
cleanup.handlers.length = 0;
|
|
36
|
-
|
|
37
|
-
// Clean up focus trap
|
|
38
|
-
if (cleanup.state.focusTrapHandler) {
|
|
39
|
-
document.removeEventListener('keydown', cleanup.state.focusTrapHandler);
|
|
40
|
-
cleanup.state.focusTrapHandler = null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Clean up global document listeners
|
|
44
|
-
if (cleanup.state.dropdownKeydownHandler) {
|
|
45
|
-
document.removeEventListener('keydown', cleanup.state.dropdownKeydownHandler);
|
|
46
|
-
cleanup.state.dropdownKeydownHandler = null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (cleanup.state.menuKeydownHandler) {
|
|
50
|
-
document.removeEventListener('keydown', cleanup.state.menuKeydownHandler);
|
|
51
|
-
cleanup.state.menuKeydownHandler = null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Remove body overflow class if present
|
|
55
|
-
document.body.classList.remove("u-overflow-hidden");
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// Desktop dropdown system
|
|
62
|
-
function setupDynamicDropdowns(addObserver, addHandler) {
|
|
63
|
-
const dropdownWrappers = document.querySelectorAll(
|
|
64
|
-
'[data-hs-nav="dropdown"]',
|
|
65
|
-
);
|
|
66
|
-
const allDropdowns = [];
|
|
67
|
-
|
|
68
|
-
const closeAllDropdowns = (exceptWrapper = null) => {
|
|
69
|
-
allDropdowns.forEach((dropdown) => {
|
|
70
|
-
if (dropdown.wrapper !== exceptWrapper && dropdown.isOpen) {
|
|
71
|
-
dropdown.closeDropdown();
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
dropdownWrappers.forEach((wrapper) => {
|
|
77
|
-
const clickableElement = wrapper.querySelector('[data-site-clickable="element"]');
|
|
78
|
-
const toggle = clickableElement ? clickableElement.children[0] : null;
|
|
79
|
-
const dropdownList = wrapper.querySelector('[data-hs-nav="dropdown-list"]');
|
|
80
|
-
|
|
81
|
-
if (!toggle || !dropdownList) {
|
|
82
|
-
console.warn("Dropdown wrapper missing required elements:", wrapper);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const toggleText = toggle.textContent?.trim() || "dropdown";
|
|
87
|
-
const sanitizedText = sanitizeForID(toggleText);
|
|
88
|
-
const toggleId = `navbar-dropdown-${sanitizedText}-toggle`;
|
|
89
|
-
const listId = `navbar-dropdown-${sanitizedText}-list`;
|
|
90
|
-
|
|
91
|
-
toggle.id = toggleId;
|
|
92
|
-
toggle.setAttribute("aria-haspopup", "menu");
|
|
93
|
-
toggle.setAttribute("aria-expanded", "false");
|
|
94
|
-
toggle.setAttribute("aria-controls", listId);
|
|
95
|
-
|
|
96
|
-
dropdownList.id = listId;
|
|
97
|
-
dropdownList.setAttribute("role", "menu");
|
|
98
|
-
dropdownList.setAttribute("aria-hidden", "true");
|
|
99
|
-
|
|
100
|
-
const clickableItems = dropdownList.querySelectorAll('[data-site-clickable="element"]');
|
|
101
|
-
const menuItems = Array.from(clickableItems).map(el => el.children[0]).filter(Boolean);
|
|
102
|
-
menuItems.forEach((item, index) => {
|
|
103
|
-
item.setAttribute("role", "menuitem");
|
|
104
|
-
item.setAttribute("tabindex", "-1");
|
|
105
|
-
|
|
106
|
-
// Add context for first item to help screen readers understand dropdown content
|
|
107
|
-
if (index === 0) {
|
|
108
|
-
const toggleText = toggle.textContent?.trim() || "menu";
|
|
109
|
-
const existingLabel = item.getAttribute("aria-label");
|
|
110
|
-
if (!existingLabel) {
|
|
111
|
-
item.setAttribute("aria-label", `${item.textContent?.trim()}, ${toggleText} submenu`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
let currentMenuItemIndex = -1;
|
|
117
|
-
|
|
118
|
-
// Set initial ARIA states
|
|
119
|
-
updateARIAStates();
|
|
120
|
-
|
|
121
|
-
// Check if dropdown is open by looking for is-active class on wrapper
|
|
122
|
-
function isDropdownOpen() {
|
|
123
|
-
return wrapper.classList.contains('is-active');
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Update ARIA states based on current visual state
|
|
127
|
-
function updateARIAStates() {
|
|
128
|
-
const isOpen = isDropdownOpen();
|
|
129
|
-
const wasOpen = toggle.getAttribute("aria-expanded") === "true";
|
|
130
|
-
|
|
131
|
-
// If dropdown is closing (was open, now closed), focus the toggle first
|
|
132
|
-
if (wasOpen && !isOpen && dropdownList.contains(document.activeElement)) {
|
|
133
|
-
toggle.focus();
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
toggle.setAttribute("aria-expanded", isOpen ? "true" : "false");
|
|
137
|
-
dropdownList.setAttribute("aria-hidden", isOpen ? "false" : "true");
|
|
138
|
-
menuItems.forEach((item) => {
|
|
139
|
-
item.setAttribute("tabindex", isOpen ? "0" : "-1");
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
if (!isOpen) {
|
|
143
|
-
currentMenuItemIndex = -1;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Monitor for class changes and update ARIA states
|
|
148
|
-
const observer = new MutationObserver(() => {
|
|
149
|
-
updateARIAStates();
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
observer.observe(wrapper, {
|
|
153
|
-
attributes: true,
|
|
154
|
-
attributeFilter: ['class']
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
addObserver(observer);
|
|
158
|
-
|
|
159
|
-
// Hover interactions now handled entirely by native Webflow
|
|
160
|
-
|
|
161
|
-
// Add keyboard interactions that trigger programmatic mouse events
|
|
162
|
-
const toggleKeydownHandler = function (e) {
|
|
163
|
-
if (e.key === "ArrowDown") {
|
|
164
|
-
e.preventDefault();
|
|
165
|
-
|
|
166
|
-
if (!isDropdownOpen()) {
|
|
167
|
-
// Trigger programmatic mouseenter to open dropdown
|
|
168
|
-
const mouseEnterEvent = new MouseEvent("mouseenter", {
|
|
169
|
-
bubbles: false,
|
|
170
|
-
cancelable: false,
|
|
171
|
-
view: window,
|
|
172
|
-
relatedTarget: null
|
|
173
|
-
});
|
|
174
|
-
wrapper.dispatchEvent(mouseEnterEvent);
|
|
175
|
-
|
|
176
|
-
// Focus first menu item after a brief delay
|
|
177
|
-
if (menuItems.length > 0) {
|
|
178
|
-
setTimeout(() => {
|
|
179
|
-
currentMenuItemIndex = 0;
|
|
180
|
-
menuItems[0].focus();
|
|
181
|
-
}, 100);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
} else if (e.key === " ") {
|
|
185
|
-
e.preventDefault();
|
|
186
|
-
|
|
187
|
-
if (!isDropdownOpen()) {
|
|
188
|
-
// Trigger programmatic mouseenter to open dropdown
|
|
189
|
-
const mouseEnterEvent = new MouseEvent("mouseenter", {
|
|
190
|
-
bubbles: false,
|
|
191
|
-
cancelable: false,
|
|
192
|
-
view: window,
|
|
193
|
-
relatedTarget: null
|
|
194
|
-
});
|
|
195
|
-
wrapper.dispatchEvent(mouseEnterEvent);
|
|
196
|
-
|
|
197
|
-
// Focus first menu item after a brief delay
|
|
198
|
-
if (menuItems.length > 0) {
|
|
199
|
-
setTimeout(() => {
|
|
200
|
-
currentMenuItemIndex = 0;
|
|
201
|
-
menuItems[0].focus();
|
|
202
|
-
}, 100);
|
|
203
|
-
}
|
|
204
|
-
} else {
|
|
205
|
-
// Trigger mouse leave to close dropdown
|
|
206
|
-
const mouseOutEvent = new MouseEvent("mouseout", {
|
|
207
|
-
bubbles: true,
|
|
208
|
-
cancelable: false,
|
|
209
|
-
view: window,
|
|
210
|
-
relatedTarget: document.body
|
|
211
|
-
});
|
|
212
|
-
wrapper.dispatchEvent(mouseOutEvent);
|
|
213
|
-
|
|
214
|
-
const mouseLeaveEvent = new MouseEvent("mouseleave", {
|
|
215
|
-
bubbles: false,
|
|
216
|
-
cancelable: false,
|
|
217
|
-
view: window,
|
|
218
|
-
relatedTarget: document.body
|
|
219
|
-
});
|
|
220
|
-
wrapper.dispatchEvent(mouseLeaveEvent);
|
|
221
|
-
}
|
|
222
|
-
} else if (e.key === "ArrowUp") {
|
|
223
|
-
e.preventDefault();
|
|
224
|
-
|
|
225
|
-
if (!isDropdownOpen()) {
|
|
226
|
-
// Trigger programmatic mouseenter to open dropdown
|
|
227
|
-
const mouseEnterEvent = new MouseEvent("mouseenter", {
|
|
228
|
-
bubbles: false,
|
|
229
|
-
cancelable: false,
|
|
230
|
-
view: window,
|
|
231
|
-
relatedTarget: null
|
|
232
|
-
});
|
|
233
|
-
wrapper.dispatchEvent(mouseEnterEvent);
|
|
234
|
-
|
|
235
|
-
// Focus last menu item after a brief delay
|
|
236
|
-
if (menuItems.length > 0) {
|
|
237
|
-
setTimeout(() => {
|
|
238
|
-
currentMenuItemIndex = menuItems.length - 1;
|
|
239
|
-
menuItems[currentMenuItemIndex].focus();
|
|
240
|
-
}, 100);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
} else if (e.key === "Escape") {
|
|
244
|
-
e.preventDefault();
|
|
245
|
-
|
|
246
|
-
if (isDropdownOpen()) {
|
|
247
|
-
// Trigger mouse leave to close dropdown
|
|
248
|
-
const mouseOutEvent = new MouseEvent("mouseout", {
|
|
249
|
-
bubbles: true,
|
|
250
|
-
cancelable: false,
|
|
251
|
-
view: window,
|
|
252
|
-
relatedTarget: document.body
|
|
253
|
-
});
|
|
254
|
-
wrapper.dispatchEvent(mouseOutEvent);
|
|
255
|
-
|
|
256
|
-
const mouseLeaveEvent = new MouseEvent("mouseleave", {
|
|
257
|
-
bubbles: false,
|
|
258
|
-
cancelable: false,
|
|
259
|
-
view: window,
|
|
260
|
-
relatedTarget: document.body
|
|
261
|
-
});
|
|
262
|
-
wrapper.dispatchEvent(mouseLeaveEvent);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
addHandler(toggle, "keydown", toggleKeydownHandler);
|
|
268
|
-
|
|
269
|
-
// Handle navigation within open dropdown
|
|
270
|
-
const dropdownKeydownHandler = function (e) {
|
|
271
|
-
if (!isDropdownOpen()) return;
|
|
272
|
-
if (!wrapper.contains(document.activeElement)) return;
|
|
273
|
-
|
|
274
|
-
if (e.key === "ArrowDown") {
|
|
275
|
-
e.preventDefault();
|
|
276
|
-
if (currentMenuItemIndex < menuItems.length - 1) {
|
|
277
|
-
currentMenuItemIndex++;
|
|
278
|
-
menuItems[currentMenuItemIndex].focus();
|
|
279
|
-
}
|
|
280
|
-
} else if (e.key === "ArrowUp") {
|
|
281
|
-
e.preventDefault();
|
|
282
|
-
if (currentMenuItemIndex === 0) {
|
|
283
|
-
// On first item, trigger mouse leave to close dropdown
|
|
284
|
-
const mouseOutEvent = new MouseEvent("mouseout", {
|
|
285
|
-
bubbles: true,
|
|
286
|
-
cancelable: false,
|
|
287
|
-
view: window,
|
|
288
|
-
relatedTarget: document.body
|
|
289
|
-
});
|
|
290
|
-
wrapper.dispatchEvent(mouseOutEvent);
|
|
291
|
-
|
|
292
|
-
const mouseLeaveEvent = new MouseEvent("mouseleave", {
|
|
293
|
-
bubbles: false,
|
|
294
|
-
cancelable: false,
|
|
295
|
-
view: window,
|
|
296
|
-
relatedTarget: document.body
|
|
297
|
-
});
|
|
298
|
-
wrapper.dispatchEvent(mouseLeaveEvent);
|
|
299
|
-
|
|
300
|
-
// Focus back to toggle
|
|
301
|
-
toggle.focus();
|
|
302
|
-
currentMenuItemIndex = -1;
|
|
303
|
-
} else if (currentMenuItemIndex > 0) {
|
|
304
|
-
currentMenuItemIndex--;
|
|
305
|
-
menuItems[currentMenuItemIndex].focus();
|
|
306
|
-
} else {
|
|
307
|
-
// Go back to toggle
|
|
308
|
-
toggle.focus();
|
|
309
|
-
currentMenuItemIndex = -1;
|
|
310
|
-
}
|
|
311
|
-
} else if (e.key === "Escape") {
|
|
312
|
-
e.preventDefault();
|
|
313
|
-
|
|
314
|
-
// Trigger mouse leave to close dropdown
|
|
315
|
-
const mouseOutEvent = new MouseEvent("mouseout", {
|
|
316
|
-
bubbles: true,
|
|
317
|
-
cancelable: false,
|
|
318
|
-
view: window,
|
|
319
|
-
relatedTarget: document.body
|
|
320
|
-
});
|
|
321
|
-
wrapper.dispatchEvent(mouseOutEvent);
|
|
322
|
-
|
|
323
|
-
const mouseLeaveEvent = new MouseEvent("mouseleave", {
|
|
324
|
-
bubbles: false,
|
|
325
|
-
cancelable: false,
|
|
326
|
-
view: window,
|
|
327
|
-
relatedTarget: document.body
|
|
328
|
-
});
|
|
329
|
-
wrapper.dispatchEvent(mouseLeaveEvent);
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
addHandler(document, "keydown", dropdownKeydownHandler);
|
|
334
|
-
|
|
335
|
-
allDropdowns.push({
|
|
336
|
-
wrapper,
|
|
337
|
-
isOpen: isDropdownOpen,
|
|
338
|
-
closeDropdown: () => {
|
|
339
|
-
// closeDropdown now handled by native Webflow interactions
|
|
340
|
-
// This is kept for API compatibility but does nothing
|
|
341
|
-
},
|
|
342
|
-
toggle,
|
|
343
|
-
dropdownList,
|
|
344
|
-
});
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
const focusinHandler = function (e) {
|
|
348
|
-
allDropdowns.forEach((dropdown) => {
|
|
349
|
-
if (dropdown.isOpen() && !dropdown.wrapper.contains(e.target)) {
|
|
350
|
-
dropdown.closeDropdown();
|
|
351
|
-
}
|
|
352
|
-
});
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
addHandler(document, "focusin", focusinHandler);
|
|
356
|
-
|
|
357
|
-
addDesktopArrowNavigation(addHandler);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Desktop left/right arrow navigation
|
|
361
|
-
function addDesktopArrowNavigation(addHandler) {
|
|
362
|
-
const keydownHandler = function (e) {
|
|
363
|
-
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
|
|
364
|
-
|
|
365
|
-
const menu = document.querySelector('[data-hs-nav="menu"]');
|
|
366
|
-
if (menu && menu.contains(document.activeElement)) return;
|
|
367
|
-
|
|
368
|
-
const navbar = document.querySelector('[data-hs-nav="wrapper"]');
|
|
369
|
-
|
|
370
|
-
if (!navbar || !navbar.contains(document.activeElement)) return;
|
|
371
|
-
|
|
372
|
-
const openDropdownList = navbar.querySelector(
|
|
373
|
-
'[aria-hidden="false"][role="menu"]',
|
|
374
|
-
);
|
|
375
|
-
if (openDropdownList && openDropdownList.contains(document.activeElement))
|
|
376
|
-
return;
|
|
377
|
-
|
|
378
|
-
e.preventDefault();
|
|
379
|
-
|
|
380
|
-
const clickableElements = navbar.querySelectorAll('[data-site-clickable="element"]');
|
|
381
|
-
const allNavbarElements = Array.from(clickableElements).map(el => el.children[0]).filter(Boolean);
|
|
382
|
-
const focusableElements = Array.from(allNavbarElements).filter((el) => {
|
|
383
|
-
if (el.getAttribute("tabindex") === "-1") return false;
|
|
384
|
-
|
|
385
|
-
const isInDropdownList = el.closest('[role="menu"]');
|
|
386
|
-
if (isInDropdownList) return false;
|
|
387
|
-
|
|
388
|
-
const isInMenu = el.closest('[data-hs-nav="menu"]');
|
|
389
|
-
if (isInMenu) return false;
|
|
390
|
-
|
|
391
|
-
const isInSkipLink = el.closest('[data-hs-nav="skip-link"]');
|
|
392
|
-
if (isInSkipLink) return false;
|
|
393
|
-
|
|
394
|
-
const computedStyle = window.getComputedStyle(el);
|
|
395
|
-
const isHidden =
|
|
396
|
-
computedStyle.display === "none" ||
|
|
397
|
-
computedStyle.visibility === "hidden" ||
|
|
398
|
-
computedStyle.opacity === "0" ||
|
|
399
|
-
el.offsetWidth === 0 ||
|
|
400
|
-
el.offsetHeight === 0;
|
|
401
|
-
if (isHidden) return false;
|
|
402
|
-
|
|
403
|
-
let parent = el.parentElement;
|
|
404
|
-
while (parent && parent !== navbar) {
|
|
405
|
-
const parentStyle = window.getComputedStyle(parent);
|
|
406
|
-
const parentHidden =
|
|
407
|
-
parentStyle.display === "none" ||
|
|
408
|
-
parentStyle.visibility === "hidden" ||
|
|
409
|
-
parent.offsetWidth === 0 ||
|
|
410
|
-
parent.offsetHeight === 0;
|
|
411
|
-
if (parentHidden) return false;
|
|
412
|
-
parent = parent.parentElement;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return true;
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
const currentIndex = focusableElements.indexOf(document.activeElement);
|
|
419
|
-
if (currentIndex === -1) return;
|
|
420
|
-
|
|
421
|
-
if (e.key === "ArrowRight") {
|
|
422
|
-
if (currentIndex < focusableElements.length - 1) {
|
|
423
|
-
const nextIndex = currentIndex + 1;
|
|
424
|
-
focusableElements[nextIndex].focus();
|
|
425
|
-
}
|
|
426
|
-
} else {
|
|
427
|
-
if (currentIndex > 0) {
|
|
428
|
-
const nextIndex = currentIndex - 1;
|
|
429
|
-
focusableElements[nextIndex].focus();
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
addHandler(document, "keydown", keydownHandler);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// Menu button system with modal-like functionality
|
|
438
|
-
function setupMenuButton(addHandler, addObserver) {
|
|
439
|
-
const menuButtons = document.querySelectorAll('[data-hs-nav="menu-button"]');
|
|
440
|
-
const menu = document.querySelector('[data-hs-nav="menu"]');
|
|
441
|
-
|
|
442
|
-
if (!menuButtons.length || !menu) return;
|
|
443
|
-
|
|
444
|
-
const menuId = `menu-${Date.now()}`;
|
|
445
|
-
|
|
446
|
-
menu.id = menuId;
|
|
447
|
-
menu.setAttribute("role", "dialog");
|
|
448
|
-
menu.setAttribute("aria-modal", "true");
|
|
449
|
-
|
|
450
|
-
function shouldPreventMenu() {
|
|
451
|
-
const menuHideElement = document.querySelector('[data-hs-nav="menu-hide"]');
|
|
452
|
-
if (!menuHideElement) return false;
|
|
453
|
-
|
|
454
|
-
const computedStyle = window.getComputedStyle(menuHideElement);
|
|
455
|
-
return computedStyle.display === "none";
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function createFocusTrap(menuButton) {
|
|
459
|
-
const navbarWrapper = document.querySelector('[data-hs-nav="wrapper"]');
|
|
460
|
-
|
|
461
|
-
if (!navbarWrapper) return;
|
|
462
|
-
|
|
463
|
-
const focusTrapHandler = (e) => {
|
|
464
|
-
if (e.key === 'Tab') {
|
|
465
|
-
const clickableElements = navbarWrapper.querySelectorAll('[data-site-clickable="element"]');
|
|
466
|
-
const clickableItems = Array.from(clickableElements).map(el => el.children[0]).filter(Boolean);
|
|
467
|
-
const formElements = navbarWrapper.querySelectorAll('input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
|
468
|
-
const focusableArray = [...clickableItems, ...Array.from(formElements)];
|
|
469
|
-
const firstElement = focusableArray[0];
|
|
470
|
-
const lastElement = focusableArray[focusableArray.length - 1];
|
|
471
|
-
|
|
472
|
-
if (e.shiftKey) {
|
|
473
|
-
// Shift+Tab: moving backwards
|
|
474
|
-
if (document.activeElement === firstElement) {
|
|
475
|
-
e.preventDefault();
|
|
476
|
-
lastElement.focus();
|
|
477
|
-
}
|
|
478
|
-
} else {
|
|
479
|
-
// Tab: moving forwards
|
|
480
|
-
if (document.activeElement === lastElement) {
|
|
481
|
-
e.preventDefault();
|
|
482
|
-
firstElement.focus();
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
document.addEventListener('keydown', focusTrapHandler);
|
|
489
|
-
return focusTrapHandler;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function removeFocusTrap(focusTrapHandler) {
|
|
493
|
-
if (focusTrapHandler) {
|
|
494
|
-
document.removeEventListener('keydown', focusTrapHandler);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
menuButtons.forEach(menuButton => {
|
|
499
|
-
menuButton.setAttribute("aria-expanded", "false");
|
|
500
|
-
menuButton.setAttribute("aria-controls", menuId);
|
|
501
|
-
menuButton.setAttribute("aria-label", "Open navigation menu");
|
|
502
|
-
|
|
503
|
-
let focusTrapHandler = null;
|
|
504
|
-
|
|
505
|
-
// Check if menu is open by looking for is-active class on button
|
|
506
|
-
function isMenuOpen() {
|
|
507
|
-
return menuButton.classList.contains('is-active');
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// Update ARIA states and menu behavior based on current visual state
|
|
511
|
-
function updateMenuState() {
|
|
512
|
-
const isOpen = isMenuOpen();
|
|
513
|
-
const wasOpen = menuButton.getAttribute("aria-expanded") === "true";
|
|
514
|
-
|
|
515
|
-
if (isOpen && !wasOpen) {
|
|
516
|
-
// Opening
|
|
517
|
-
document.body.classList.add("u-overflow-hidden");
|
|
518
|
-
window.lenis?.stop();
|
|
519
|
-
|
|
520
|
-
menuButtons.forEach(btn => {
|
|
521
|
-
btn.setAttribute("aria-expanded", "true");
|
|
522
|
-
btn.setAttribute("aria-label", "Close navigation menu");
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
// Create focus trap for navbar
|
|
526
|
-
focusTrapHandler = createFocusTrap(menuButton);
|
|
527
|
-
|
|
528
|
-
// Focus first menu item after menu opens
|
|
529
|
-
setTimeout(() => {
|
|
530
|
-
const firstClickable = menu.querySelector('[data-site-clickable="element"]');
|
|
531
|
-
const firstElement = firstClickable?.children[0];
|
|
532
|
-
if (firstElement) {
|
|
533
|
-
firstElement.focus();
|
|
534
|
-
}
|
|
535
|
-
}, 100);
|
|
536
|
-
} else if (!isOpen && wasOpen) {
|
|
537
|
-
// Closing
|
|
538
|
-
document.body.classList.remove("u-overflow-hidden");
|
|
539
|
-
window.lenis?.start();
|
|
540
|
-
|
|
541
|
-
// Return focus to button if focus was inside menu
|
|
542
|
-
if (menu.contains(document.activeElement)) {
|
|
543
|
-
menuButton.focus();
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
menuButtons.forEach(btn => {
|
|
547
|
-
btn.setAttribute("aria-expanded", "false");
|
|
548
|
-
btn.setAttribute("aria-label", "Open navigation menu");
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
// Remove focus trap
|
|
552
|
-
removeFocusTrap(focusTrapHandler);
|
|
553
|
-
focusTrapHandler = null;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// Set initial ARIA states
|
|
558
|
-
updateMenuState();
|
|
559
|
-
|
|
560
|
-
// Monitor for class changes and update menu state
|
|
561
|
-
const observer = new MutationObserver(() => {
|
|
562
|
-
updateMenuState();
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
observer.observe(menuButton, {
|
|
566
|
-
attributes: true,
|
|
567
|
-
attributeFilter: ['class']
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
addObserver(observer);
|
|
571
|
-
|
|
572
|
-
const clickHandler = function () {
|
|
573
|
-
// Webflow interaction handles the visual state (is-active class)
|
|
574
|
-
// MutationObserver will sync ARIA and behavior
|
|
575
|
-
};
|
|
576
|
-
|
|
577
|
-
addHandler(menuButton, "click", clickHandler);
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
function setupMenuDisplayObserver(addObserver) {
|
|
583
|
-
function handleDisplayChange() {
|
|
584
|
-
const menuHideElement = document.querySelector('[data-hs-nav="menu-hide"]');
|
|
585
|
-
if (!menuHideElement) return;
|
|
586
|
-
|
|
587
|
-
const computedStyle = window.getComputedStyle(menuHideElement);
|
|
588
|
-
const isMenuVisible = computedStyle.display !== "none";
|
|
589
|
-
|
|
590
|
-
// Get menu button to check if menu is open
|
|
591
|
-
const menuButton = document.querySelector('[data-hs-nav="menu-button"]');
|
|
592
|
-
const isMenuOpen = menuButton && menuButton.getAttribute("aria-expanded") === "true";
|
|
593
|
-
|
|
594
|
-
const shouldShowModal = isMenuVisible && isMenuOpen;
|
|
595
|
-
|
|
596
|
-
// Toggle modal effects only when menu is visible AND menu is open
|
|
597
|
-
document.body.classList.toggle("u-overflow-hidden", shouldShowModal);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
const displayObserver = new ResizeObserver(handleDisplayChange);
|
|
601
|
-
const menuHideElement = document.querySelector('[data-hs-nav="menu-hide"]');
|
|
602
|
-
if (menuHideElement) {
|
|
603
|
-
displayObserver.observe(menuHideElement);
|
|
604
|
-
addObserver(displayObserver);
|
|
605
|
-
// Initial check
|
|
606
|
-
handleDisplayChange();
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
function sanitizeForID(text) {
|
|
611
|
-
return text
|
|
612
|
-
.toLowerCase()
|
|
613
|
-
.replace(/[^a-z0-9\s]/g, "")
|
|
614
|
-
.replace(/\s+/g, "-")
|
|
615
|
-
.replace(/^-+|-+$/g, "")
|
|
616
|
-
.substring(0, 50);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// Menu ARIA setup
|
|
620
|
-
function setupMenuARIA(addHandler, addObserver) {
|
|
621
|
-
const menuContainer = document.querySelector('[data-hs-nav="menu"]');
|
|
622
|
-
if (!menuContainer) return;
|
|
623
|
-
|
|
624
|
-
const dropdownWrappers = menuContainer.querySelectorAll('[data-hs-nav="menu-dropdown"]');
|
|
625
|
-
const clickableLinks = menuContainer.querySelectorAll('[data-site-clickable="element"]');
|
|
626
|
-
const links = Array.from(clickableLinks).map(el => el.children[0]).filter(Boolean);
|
|
627
|
-
|
|
628
|
-
dropdownWrappers.forEach((wrapper) => {
|
|
629
|
-
const clickableElement = wrapper.querySelector('[data-site-clickable="element"]');
|
|
630
|
-
const button = clickableElement ? clickableElement.children[0] : null;
|
|
631
|
-
const dropdownList = wrapper.querySelector('[data-hs-nav="menu-dropdown-list"]');
|
|
632
|
-
|
|
633
|
-
if (button && dropdownList) {
|
|
634
|
-
const buttonText = button.textContent?.trim();
|
|
635
|
-
const sanitizedText = sanitizeForID(buttonText);
|
|
636
|
-
const buttonId = `navbar-menu-${sanitizedText}-toggle`;
|
|
637
|
-
const listId = `navbar-menu-${sanitizedText}-list`;
|
|
638
|
-
|
|
639
|
-
button.id = buttonId;
|
|
640
|
-
button.setAttribute("aria-expanded", "false");
|
|
641
|
-
button.setAttribute("aria-controls", listId);
|
|
642
|
-
|
|
643
|
-
dropdownList.id = listId;
|
|
644
|
-
dropdownList.setAttribute("aria-hidden", "true");
|
|
645
|
-
|
|
646
|
-
// Check if dropdown is open by looking for is-active class on wrapper
|
|
647
|
-
function isDropdownOpen() {
|
|
648
|
-
return wrapper.classList.contains('is-active');
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// Update ARIA states based on current visual state
|
|
652
|
-
function updateARIAStates() {
|
|
653
|
-
const isOpen = isDropdownOpen();
|
|
654
|
-
const wasOpen = button.getAttribute("aria-expanded") === "true";
|
|
655
|
-
|
|
656
|
-
// If dropdown is closing (was open, now closed), focus the button if focus is inside dropdown
|
|
657
|
-
if (wasOpen && !isOpen && dropdownList.contains(document.activeElement)) {
|
|
658
|
-
button.focus();
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
button.setAttribute("aria-expanded", isOpen ? "true" : "false");
|
|
662
|
-
dropdownList.setAttribute("aria-hidden", isOpen ? "false" : "true");
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// Set initial ARIA states
|
|
666
|
-
updateARIAStates();
|
|
667
|
-
|
|
668
|
-
// Monitor for class changes and update ARIA states
|
|
669
|
-
const observer = new MutationObserver(() => {
|
|
670
|
-
updateARIAStates();
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
observer.observe(wrapper, {
|
|
674
|
-
attributes: true,
|
|
675
|
-
attributeFilter: ['class']
|
|
676
|
-
});
|
|
677
|
-
|
|
678
|
-
addObserver(observer);
|
|
679
|
-
|
|
680
|
-
const clickHandler = function () {
|
|
681
|
-
// Webflow interaction handles the visual state (is-active class)
|
|
682
|
-
// MutationObserver will sync ARIA attributes
|
|
683
|
-
};
|
|
684
|
-
|
|
685
|
-
addHandler(button, "click", clickHandler);
|
|
686
|
-
}
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
links.forEach((link) => {
|
|
690
|
-
const linkText = link.textContent?.trim();
|
|
691
|
-
const sanitizedText = sanitizeForID(linkText);
|
|
692
|
-
const linkId = `navbar-menu-${sanitizedText}-link`;
|
|
693
|
-
link.id = linkId;
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
setupMenuArrowNavigation(menuContainer, addHandler);
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
// Menu arrow navigation
|
|
700
|
-
function setupMenuArrowNavigation(menuContainer, addHandler) {
|
|
701
|
-
function getFocusableElements() {
|
|
702
|
-
const clickableElements = menuContainer.querySelectorAll('[data-site-clickable="element"]');
|
|
703
|
-
const allElements = Array.from(clickableElements).map(el => el.children[0]).filter(Boolean);
|
|
704
|
-
return Array.from(allElements).filter((el) => {
|
|
705
|
-
// Check if element or any ancestor has aria-hidden="true"
|
|
706
|
-
let current = el;
|
|
707
|
-
while (current && current !== menuContainer) {
|
|
708
|
-
if (current.getAttribute("aria-hidden") === "true") {
|
|
709
|
-
return false;
|
|
710
|
-
}
|
|
711
|
-
current = current.parentElement;
|
|
712
|
-
}
|
|
713
|
-
return true;
|
|
714
|
-
});
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
let currentFocusIndex = -1;
|
|
718
|
-
|
|
719
|
-
const keydownHandler = function (e) {
|
|
720
|
-
const focusableElements = getFocusableElements();
|
|
721
|
-
if (focusableElements.length === 0) return;
|
|
722
|
-
|
|
723
|
-
const activeElement = document.activeElement;
|
|
724
|
-
currentFocusIndex = focusableElements.indexOf(activeElement);
|
|
725
|
-
|
|
726
|
-
if (e.key === "ArrowDown") {
|
|
727
|
-
e.preventDefault();
|
|
728
|
-
if (currentFocusIndex < focusableElements.length - 1) {
|
|
729
|
-
currentFocusIndex = currentFocusIndex + 1;
|
|
730
|
-
focusableElements[currentFocusIndex].focus();
|
|
731
|
-
}
|
|
732
|
-
} else if (e.key === "ArrowUp") {
|
|
733
|
-
e.preventDefault();
|
|
734
|
-
if (currentFocusIndex > 0) {
|
|
735
|
-
currentFocusIndex = currentFocusIndex - 1;
|
|
736
|
-
focusableElements[currentFocusIndex].focus();
|
|
737
|
-
}
|
|
738
|
-
} else if (e.key === "ArrowRight") {
|
|
739
|
-
e.preventDefault();
|
|
740
|
-
if (
|
|
741
|
-
activeElement.tagName === "BUTTON" &&
|
|
742
|
-
activeElement.hasAttribute("aria-controls")
|
|
743
|
-
) {
|
|
744
|
-
const isExpanded =
|
|
745
|
-
activeElement.getAttribute("aria-expanded") === "true";
|
|
746
|
-
if (!isExpanded) {
|
|
747
|
-
activeElement.click();
|
|
748
|
-
}
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
} else if (e.key === "ArrowLeft") {
|
|
752
|
-
e.preventDefault();
|
|
753
|
-
if (
|
|
754
|
-
activeElement.tagName === "BUTTON" &&
|
|
755
|
-
activeElement.hasAttribute("aria-controls")
|
|
756
|
-
) {
|
|
757
|
-
const isExpanded =
|
|
758
|
-
activeElement.getAttribute("aria-expanded") === "true";
|
|
759
|
-
if (isExpanded) {
|
|
760
|
-
activeElement.click();
|
|
761
|
-
}
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
} else if (e.key === "Home") {
|
|
765
|
-
e.preventDefault();
|
|
766
|
-
currentFocusIndex = 0;
|
|
767
|
-
focusableElements[0].focus();
|
|
768
|
-
} else if (e.key === "End") {
|
|
769
|
-
e.preventDefault();
|
|
770
|
-
currentFocusIndex = focusableElements.length - 1;
|
|
771
|
-
focusableElements[focusableElements.length - 1].focus();
|
|
772
|
-
} else if (e.key === " " && activeElement.tagName === "A") {
|
|
773
|
-
e.preventDefault();
|
|
774
|
-
} else if (e.key === "Escape") {
|
|
775
|
-
const menuButton = document.querySelector(
|
|
776
|
-
'[data-hs-nav="menu-button"]',
|
|
777
|
-
);
|
|
778
|
-
if (menuButton) {
|
|
779
|
-
menuButton.click();
|
|
780
|
-
menuButton.focus();
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
};
|
|
784
|
-
|
|
785
|
-
addHandler(menuContainer, "keydown", keydownHandler);
|
|
786
|
-
}
|