1/** 2 * @file mega-menu.js 3 * 4 * JS File for toggle elements. 5 */ 6(function (Drupal, once) { 7 8 const toggleSelector = '.mega-menu [data-toggle]'; 9 10 /** 11 * @Behavior 12 * 13 * Open/close target element by an ID set in the aria-controls attribute. 14 */ 15 Drupal.behaviors.megaMenuToggleComponent = { 16 onceName: 'mega-menu-toggle', 17 attach(context) { 18 once(this.onceName, context.querySelectorAll(toggleSelector), context).forEach(function (toggle) { 19 ['click', 'touch'].forEach(function (e) { 20 toggle.addEventListener(e, function (event) { 21 if (toggle.getAttribute('mega-menu-closing')) { 22 toggle.removeAttribute('mega-menu-closing'); 23 return; 24 } 25 26 // Close all other open items. 27 let targetId = toggle.getAttribute('aria-controls'); 28 let allOthersOpenSelector = '.mega-menu [aria-expanded="true"]'; 29 context.querySelectorAll(allOthersOpenSelector).forEach(function (btn) { 30 if (btn.getAttribute('aria-expanded') == 'true' && btn.getAttribute('aria-controls') != targetId) { 31 // Prevent infinite loop and/or closing current item. 32 btn.setAttribute('mega-menu-closing', true); 33 btn.click(); 34 } 35 }); 36 }); 37 }); 38 }); 39 40 once(this.onceName, 'body', context).forEach(function (body) { 41 ['click', 'touch'].forEach(function (e) { 42 body.addEventListener(e, function (event) { 43 if (!event.target.closest('.mega-menu__list__item')) { 44 let allOthersOpenSelector = '.mega-menu__close[aria-expanded="true"]'; 45 body.querySelectorAll(allOthersOpenSelector).forEach(function (btn) { 46 btn.click(); 47 }); 48 } 49 }); 50 }); 51 }); 52 }, 53 detach: function detach(context, settings, trigger) { 54 if (trigger === "unload") { 55 once.remove(this.onceName, context.querySelectorAll(toggleSelector)); 56 once.remove(this.onceName, 'body'); 57 } 58 }, 59 }; 60 61 Drupal.behaviors.megaMenuKeyboardNavigation = { 62 onceName: 'mega-menu-keyboard-nav', 63 selector: '[data-mega-menu-keyboard-nav="true"]', 64 65 attach(context) { 66 const self = this; 67 68 once(this.onceName, context.querySelectorAll(this.selector), context).forEach((megaMenu) => { 69 const menuItems = megaMenu.querySelectorAll('.mega-menu__list__item'); 70 71 menuItems.forEach((menuItem) => { 72 const toggleButton = menuItem.querySelector('.mega-menu__link'); 73 const submenuWrapper = menuItem.querySelector('.mega-menu__wrapper'); 74 75 if (toggleButton && submenuWrapper) { 76 // Close submenu on actions on first level. 77 // Espace key press or tab key press. 78 toggleButton.addEventListener('keydown', (event) => { 79 if ( 80 (event.key === 'Escape') || 81 (event.shiftKey && event.key == 'Tab') 82 ) { 83 self.closeSubmenu(); 84 } 85 }); 86 87 // Close current submenu on first second level item or last level item. 88 // Close current submenu on escape key down. 89 submenuWrapper.addEventListener('keydown', (event) => { 90 const focusableElements = self.getFocusableElements(submenuWrapper); 91 const firstEl = focusableElements[0]; 92 const lastEl = focusableElements[focusableElements.length - 1]; 93 94 if (event.key === 'Tab') { 95 if ( 96 (event.shiftKey && event.target === firstEl) || 97 (!event.shiftKey && event.target === lastEl) 98 ) { 99 self.closeSubmenu(); 100 } 101 } else if (event.key === 'Escape') { 102 self.closeSubmenu(); 103 toggleButton.focus(); 104 } 105 }); 106 } 107 }); 108 }); 109 }, 110 111 getFocusableElements(container) { 112 return Array.from( 113 container.querySelectorAll( 114 'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not
114([tabindex="-1"])' 115 ), 116 ).filter(el => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden')); 117 }, 118 119 closeSubmenu() { 120 let allOthersOpenSelector = '.mega-menu__close[aria-expanded="true"]'; 121 document.querySelectorAll(allOthersOpenSelector).forEach(function (btn) { 122 btn.click(); 123 }); 124 }, 125 }; 126})(Drupal, once);
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.