1(function (Drupal, once) { 2 Drupal.behaviors.inklusionNavigation = { 3 attach(context) { 4 once('inklusion-navigation', '.nav-toggle', context).forEach((button) => { 5 const navId = button.getAttribute('aria-controls'); 6 const nav = navId ? document.getElementById(navId) : null; 7 if (!nav) { 8 return; 9 } 10 11 const originalParent = nav.parentNode; 12 const placeholder = document.createComment('primary-navigation-placeholder'); 13 originalParent.insertBefore(placeholder, nav); 14 let restoreTimer = null; 15 16 const isOffCanvasViewport = () => window.matchMedia('(max-width: 991.98px)').matches; 17 18 // Eigene Kopfzeile für den portalierten Off-Canvas-Drawer. Der 19 // ursprüngliche Toggle verbleibt im Header; im geöffneten Drawer 20 // steht zusätzlich ein gut erreichbarer SchlieÃen-Button bereit. 21 let drawerHeader = nav.querySelector('.offcanvas-navigation__header'); 22 if (!drawerHeader) { 23 drawerHeader = document.createElement('div'); 24 drawerHeader.className = 'offcanvas-navigation__header'; 25 26 const drawerClose = document.createElement('button'); 27 drawerClose.className = 'offcanvas-navigation__close'; 28 drawerClose.type = 'button'; 29 drawerClose.setAttribute('aria-label', Drupal.t('Menü schlieÃen')); 30 drawerClose.innerHTML = '<span>Menü</span><span class="offcanvas-navigation__close-icon" aria-hidden="true"></span>'; 31 32 drawerHeader.append(drawerClose); 33 nav.prepend(drawerHeader); 34 } 35 36 const moveToBody = () => { 37 if (nav.parentNode !== document.body) { 38 document.body.appendChild(nav); 39 } 40 nav.classList.add('is-offcanvas-navigation'); 41 }; 42 43 const restoreToHeader = () => { 44 window.clearTimeout(restoreTimer); 45 if (nav.parentNode !== originalParent) { 46 originalParent.insertBefore(nav, placeholder.nextSibling); 47 } 48 nav.classList.remove('is-offcanvas-navigation'); 49 }; 50 51 const open = () => { 52 if (!isOffCanvasViewport()) { 53 return; 54 } 55 window.clearTimeout(restoreTimer); 56 moveToBody(); 57 // Ein Frame Abstand stellt sicher, dass der Browser den geschlossenen 58 // Drawer zuerst layoutet und anschlieÃend sichtbar animiert. 59 window.requestAnimationFrame(() => { 60 window.requestAnimationFrame(() => { 61 nav.classList.add('is-open'); 62 document.documentElement.classList.add('nav-is-open'); 63 button.setAttribute('aria-expanded', 'true'); 64 }); 65 }); 66 }; 67 68 const close = ({ immediate = false } = {}) => { 69 nav.classList.remove('is-open'); 70 document.documentElement.classList.remove('nav-is-open'); 71 button.setAttribute('aria-expanded', 'false'); 72 73 if (immediate) { 74 restoreToHeader(); 75 return; 76 } 77 78 restoreTimer = window.setTimeout(restoreToHeader, 500); 79 }; 80 81 const toggle = () => { 82 if (nav.classList.contains('is-open')) { 83 close(); 84 } 85 else { 86 open(); 87 } 88 }; 89 90 button.addEventListener('click', (event) => { 91 event.preventDefault(); 92 event.stopPropagation(); 93 toggle(); 94 }); 95 96 const drawerClose = drawerHeader.querySelector('.offcanvas-navigation__close'); 97 if (drawerClose) { 98 drawerClose.addEventListener('click', (event) => { 99 event.preventDefault(); 100 close(); 101 button.focus(); 102 }); 103 } 104 105 nav.addEventListener('click', (event) => { 106 const link = event.target.closest('a'); 107 if (link && isOffCanvasViewport()) { 108 close(); 109 } 110 }); 111 112 document.addEventListener('click', (event) => { 113 if (!nav.classList.contains('is-open')) { 114 return; 115 } 116 if (nav.contains(event.target) || button.contains(event.target)) { 117 return; 118 } 119 close(); 120 }); 121 122 document.addEventListener('keydown', (event) => { 123 if (event.key === 'Escape' && nav.classList.contains('is-open')) { 124 close(); 125 button.focus(); 126 } 127 }); 128 129 window.addEventListener('resize', () => { 130 if (!isOffCanvasViewport()) { 131 close({ immediate: true }); 132 } 133 }); 134 }); 135 136 once('inklusion-scroll-header', 'body', context).forEach(() => { 137 const documentElement = document.documentElement; 138 139 // Hysterese verhindert das Flackern des kompakten Headers. 140 const compactHeaderStart = 80; 141 const compactHeaderEnd = 20; 142 143 let isCompact = documentElement.classList.contains('site-is-scrolled'); 144 145 const updateHeaderState = () => { 146 const scrollPosition = 147 window.scrollY || 148 document.documentElement.scrollTop || 149 0; 150 151 if (!isCompact && scrollPosition >= compactHeaderStart) { 152 isCompact = true; 153 documentElement.classList.add('site-is-scrolled'); 154 } 155 else if (isCompact && scrollPosition <= compactHeaderEnd) { 156 isCompact = false;
157 documentElement.classList.remove('site-is-scrolled'); 158 } 159 }; 160 161 updateHeaderState(); 162 163 window.addEventListener('scroll', updateHeaderState, { 164 passive: true, 165 }); 166 }); 167 } 168 }; 169})(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.