1import {setAriaExpanded, setElementHidden} from '../core/aria.js'; 2 3const createMobileNavState = () => { 4 const root = document.querySelector('.mobile-nav'); 5 if (!(root instanceof HTMLElement)) { 6 return null; 7 } 8 9 const toggleButton = root.querySelector('[data-mobile-menu-toggle]'); 10 const panel = root.querySelector('[data-mobile-menu-panel]'); 11 if (!(toggleButton instanceof HTMLButtonElement) || !(panel instanceof HTMLElement)) { 12 return null; 13 } 14 15 return { 16 root, 17 toggleButton, 18 panel, 19 closeButton: root.querySelector('[data-mobile-menu-close]'), 20 submenuToggles: Array.from(root.querySelectorAll('[data-mobile-submenu-toggle]')) 21 }; 22}; 23 24const closeAllMobileSubmenus = (state) => { 25 state.submenuToggles.forEach((toggle) => { 26 if (!(toggle instanceof HTMLButtonElement)) { 27 return; 28 } 29 30 const panelId = toggle.getAttribute('aria-controls'); 31 if (!panelId) { 32 return; 33 } 34 35 const submenuPanel = state.root.querySelector(`#${panelId}`); 36 if (!(submenuPanel instanceof HTMLElement)) { 37 return; 38 } 39 40 toggle.classList.remove('is-open'); 41 setAriaExpanded(toggle, false); 42 submenuPanel.classList.remove('is-open'); 43 setElementHidden(submenuPanel, true); 44 }); 45}; 46 47const setMobileMenuOpenState = (state, isOpen) => { 48 state.root.classList.toggle('is-open', isOpen); 49 setAriaExpanded(state.toggleButton, isOpen); 50 setElementHidden(state.panel, !isOpen); 51 52 if (!isOpen) { 53 closeAllMobileSubmenus(state); 54 } 55}; 56 57const bindMobileMenuToggleEvents = (state) => { 58 state.toggleButton.addEventListener('click', () => { 59 const isOpen = state.root.classList.contains('is-open'); 60 setMobileMenuOpenState(state, !isOpen); 61 }); 62 63 if (state.closeButton instanceof HTMLButtonElement) { 64 state.closeButton.addEventListener('click', () => { 65 setMobileMenuOpenState(state, false); 66 }); 67 } 68}; 69 70const bindMobileSubmenuEvents = (state) => { 71 state.submenuToggles.forEach((toggle) => { 72 if (!(toggle instanceof HTMLButtonElement)) { 73 return; 74 } 75 76 const panelId = toggle.getAttribute('aria-controls'); 77 if (!panelId) { 78 return; 79 } 80 81 const submenuPanel = state.root.querySelector(`#${panelId}`); 82 if (!(submenuPanel instanceof HTMLElement)) { 83 return; 84 } 85 86 toggle.addEventListener('click', () => { 87 const shouldOpen = !toggle.classList.contains('is-open'); 88 closeAllMobileSubmenus(state); 89 if (!shouldOpen) { 90 return; 91 } 92 93 toggle.classList.add('is-open'); 94 setAriaExpanded(toggle, true); 95 submenuPanel.classList.add('is-open'); 96 setElementHidden(submenuPanel, false); 97 }); 98 }); 99}; 100 101const bindMobileGlobalEvents = (state) => { 102 document.addEventListener('keydown', (event) => { 103 if (event.key === 'Escape') { 104 setMobileMenuOpenState(state, false); 105 } 106 }); 107 108 window.addEventListener('resize', () => { 109 if (window.innerWidth >= 1024) { 110 setMobileMenuOpenState(state, false); 111 } 112 }); 113}; 114 115export const initMobileNavigation = () => { 116 const state = createMobileNavState(); 117 if (!state) { 118 return; 119 } 120 121 bindMobileMenuToggleEvents(state); 122 bindMobileSubmenuEvents(state); 123 bindMobileGlobalEvents(state); 124};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.