// tslint:disable:max-line-length // tslint:disable:match-default-export-name // tslint:disable:typedef // tslint:disable:variable-name import { SPPermission } from '@microsoft/sp-page-context'; import { MegaMenuSettingsPanel } from './MegaMenuSettings'; var MegaMenuRenderer = (function () { function MegaMenuRenderer(context, menuItems, updateCallback) { this.context = context; this.menuItems = menuItems; this.updateCallback = updateCallback; } MegaMenuRenderer.prototype.render = function (container) { var _this = this; // Clear any existing content container.innerHTML = ''; container.id = 'CustomNavigation'; // Create the main nav element var nav = document.createElement('nav'); nav.id = 'Mega-Menu'; nav.className = 'mega-menu-main'; nav.setAttribute('role', 'navigation'); nav.setAttribute('aria-label', 'Hauptnavigation'); // Create the top-level menubar var topLevelUl = document.createElement('ul'); topLevelUl.setAttribute('role', 'menubar'); // Process each top-level menu item this.menuItems.forEach(function (topLevelItem) { var topLevelLi = _this.createTopLevelItem(topLevelItem); topLevelUl.appendChild(topLevelLi); }); // Only if current user has ManageWeb permissions in this website can they access the settings if (this.context.pageContext.web.permissions.hasPermission(SPPermission.manageWeb)) { topLevelUl.appendChild(this.createSettingsItem()); } nav.appendChild(topLevelUl); container.appendChild(nav); // Attach accessibility event listeners after rendering this.attachEventListeners(); this.createScreenReaderAnnouncer(); }; MegaMenuRenderer.prototype.createSettingsItem = function () { var _this = this; var li = document.createElement('li'); li.setAttribute('role', 'none'); // Verwenden eines Links-ähnlichen Buttons damit Styling identisch zu Top-Level Items ist var btn = document.createElement('button'); btn.type = 'button'; btn.className = 'menu-item-link menu-item-settings'; // reuse same base styles btn.setAttribute('role', 'menuitem'); btn.setAttribute('tabindex', '0'); btn.setAttribute('aria-haspopup', 'false'); btn.setAttribute('aria-label', 'Einstellungen'); btn.title = 'Einstellungen'; // Nur EIN Icon: Wenn Fabric Icons geladen sind, zeigt ms-Icon das Symbol. Fallback via aria-label für Screenreader var icon = document.createElement('span'); icon.className = 'ms-Icon ms-Icon--Settings menu-item-settings__icon'; icon.setAttribute('aria-hidden', 'true'); btn.appendChild(icon); btn.addEventListener('click', function () { return _this.openSettings(); }); btn.addEventListener('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); _this.openSettings(); } }); li.appendChild(btn); return li; }; MegaMenuRenderer.prototype.openSettings = function () { if (!this._settingsPanel) { this._settingsPanel = new MegaMenuSettingsPanel(this.context, this.updateCallback); } this._settingsPanel.open(); }; // Panel logic moved to MegaMenuSettingsPanel MegaMenuRenderer.prototype.createTopLevelItem = function (item) { var li = document.createElement('li'); li.setAttribute('role', 'none'); // Create the top-level element (link or span) var topElement = this.createTopLevelElement(item); li.appendChild(topElement); // If the item has children, create the mega menu if (item.hasChildren() && item.items && item.items.length > 0) { var megaMenu = this.createMegaMenu(item); li.appendChild(megaMenu); } return li; }; MegaMenuRenderer.prototype.createTopLevelElement = function (item) { var element; if (item.url) { // Create as link element = document.createElement('a'); element.href = item.url; element.className = 'menu-item-link'; } else { // Create as span (no link) element = document.createElement('span'); element.className = 'menu-item-text'; element.setAttribute('tabindex', '0'); } element.setAttribute('role', 'menuitem'); element.setAttribute('aria-haspopup', 'true'); element.setAttribute('aria-expanded', 'false'); element.textContent = item.label; if (item.hoverText) { element.title = item.hoverText; } return element; }; MegaMenuRenderer.prototype.createMegaMenu = function (parentItem) { var _this = this; var megaMenuDiv = document.createElement('div'); megaMenuDiv.className = 'mega-menu'; megaMenuDiv.setAttribute('role', 'menu'); megaMenuDiv.setAttribute('aria-label', parentItem.label + " Unterkategorien"); var gridDiv = document.createElement('div'); gridDiv.className = 'mega-menu-grid'; // Process second-level items (categories) if (parentItem.items) { parentItem.items.forEach(function (secondLevelItem) { var categoryDiv = _this.createCategorySection(secondLevelItem); gridDiv.appendChild(categoryDiv); }); } megaMenuDiv.appendChild(gridDiv); return megaMenuDiv; }; MegaMenuRenderer.prototype.createCategorySection = function (item) { var categoryDiv = document.createElement('div'); categoryDiv.className = 'mega-menu-category'; // Create the category header (h3) var h3 = document.createElement('h3'); if (item.url) { // Category header as link var link = document.createElement('a'); link.href = item.url; link.textContent = item.label; if (item.hoverText) { link.title = item.hoverText; } h3.appendChild(link); } else { // Category header as span (no link) var span = document.createElement('span'); span.textContent = item.label; if (item.hoverText) { span.title = item.hoverText; } h3.appendChild(span); } categoryDiv.appendChild(h3); // Create the third-level items list if they exist if (item.hasChildren() && item.items && item.items.length > 0) { var ul_1 = document.createElement('ul'); item.items.forEach(function (thirdLevelItem) { var li = document.createElement('li'); var link = document.createElement('a'); link.href = thirdLevelItem.url || '#'; link.textContent = thirdLevelItem.label; if (thirdLevelItem.hoverText) { link.title = thirdLevelItem.hoverText; } li.appendChild(link); ul_1.appendChild(li); }); categoryDiv.appendChild(ul_1); } return categoryDiv; }; MegaMenuRenderer.prototype.attachEventListeners = function () { var headings = document.querySelectorAll('#Mega-Menu > ul > li > a, #Mega-Menu > ul > li > span[role="menuitem"]'); for (var i = 0; i < headings.length; i++) { var heading = headings[i]; var megaMenu = heading.nextElementSibling; if (megaMenu && megaMenu.classList.contains('mega-menu')) { this.attachKeyboardNavigation(heading, megaMenu); this.attachMouseEvents(heading, megaMenu); this.attachFocusManagement(heading, megaMenu); } } // Global keyboard navigation this.attachGlobalKeyboardNavigation(); }; MegaMenuRenderer.prototype.attachKeyboardNavigation = function (heading, megaMenu) { var _this = this; heading.addEventListener('keydown', function (e) { if (e.key === 'Enter') { // Enter: Navigation (nur bei Links ohne Mega-Menu-Override) if (heading.tagName === 'A') { // Lasse normale Link-Navigation zu (KEIN preventDefault!) return; } else { // Bei span: Toggle Menu e.preventDefault(); _this.toggleMegaMenu(heading, megaMenu); } } else if (e.key === ' ') { // Space: Toggle Dropdown (immer) e.preventDefault(); _this.toggleMegaMenu(heading, megaMenu); } else if (e.key === 'ArrowDown') { // Pfeil runter: Menü öffnen + erster Link e.preventDefault(); _this.openMegaMenu(heading, megaMenu); _this.focusFirstLink(megaMenu); } else if (e.key === 'ArrowUp') { // Pfeil hoch: Menü schließen e.preventDefault(); _this.closeMegaMenu(heading, megaMenu); } else if (e.key === 'Escape') { // Escape: Menü schließen e.preventDefault(); _this.closeMegaMenu(heading, megaMenu); heading.focus(); } }); // Click Event für Top-Level Links - NORMALE Navigation erlauben if (heading.tagName === 'A') { heading.addEventListener('click', function (e) { // Links sollen normal navigieren, nicht das Mega-Menu toglen // Wenn der User das Menu öffnen will, soll er Space oder Pfeil↓ nutzen console.log('Link geklickt:', heading.href); // KEIN preventDefault() - normale Link-Navigation }); } // Focus Management - KEIN automatisches Öffnen oder Schließen heading.addEventListener('focus', function () { console.log('Focus auf:', heading.textContent); // Einfach nur fokussiert - keine automatischen Aktionen! }); }; MegaMenuRenderer.prototype.attachMouseEvents = function (heading, megaMenu) { var _this = this; var parentLi = heading.parentElement; parentLi.addEventListener('mouseenter', function () { _this.openMegaMenu(heading, megaMenu); }); parentLi.addEventListener('mouseleave', function () { _this.closeMegaMenu(heading, megaMenu); }); }; MegaMenuRenderer.prototype.attachFocusManagement = function (heading, megaMenu) { var _this = this; // Focus-Verlust-Behandlung - vereinfacht megaMenu.addEventListener('focusout', function (e) { // Kurz warten, um zu prüfen ob Focus innerhalb des Mega-Menus bleibt setTimeout(function () { var focusedElement = document.activeElement; var isInsideThisMenu = megaMenu.contains(focusedElement); var isOnThisTrigger = focusedElement === heading; var isInAnyMegaMenu = focusedElement.closest('.mega-menu'); var isOnAnyTopLevel = focusedElement.closest('#Mega-Menu > ul > li > a, #Mega-Menu > ul > li > span'); // Nur schließen wenn Focus komplett außerhalb der Navigation ist if (!isInsideThisMenu && !isOnThisTrigger && !isInAnyMegaMenu && !isOnAnyTopLevel) { console.log('Schließe Menu wegen Focus-Verlust'); _this.closeMegaMenu(heading, megaMenu); } }, 150); }); }; MegaMenuRenderer.prototype.attachGlobalKeyboardNavigation = function () { var _this = this; document.addEventListener('keydown', function (e) { var activeElement = document.activeElement; if (e.key === 'Escape') { var openMenu = document.querySelector('.mega-menu[aria-expanded="true"]'); if (openMenu) { var triggerLink = openMenu.previousElementSibling; _this.closeMegaMenu(triggerLink, openMenu); triggerLink.focus(); } } // Tab-Navigation: Nur bei spezifischen Übergängen eingreifen if (e.key === 'Tab') { // Von Top-Level zum ersten Link im offenen Menu if (!e.shiftKey) { var currentTopLevel = activeElement.closest('#Mega-Menu > ul > li > a, #Mega-Menu > ul > li > span'); if (currentTopLevel) { var parentLi = currentTopLevel.closest('li'); var megaMenu = parentLi.querySelector('.mega-menu.js-open'); if (megaMenu) { // Nur eingreifen wenn wir vom Trigger weg-tabben e.preventDefault(); var firstLink = megaMenu.querySelector('a'); if (firstLink) { firstLink.focus(); } return; } } } // Shift+Tab: Vom ersten Link im Menu zurück zum Trigger if (e.shiftKey) { var megaMenu = activeElement.closest('.mega-menu'); if (megaMenu && megaMenu.classList.contains('js-open')) { var allLinksInMenu = megaMenu.querySelectorAll('a'); var firstLinkInMenu = allLinksInMenu[0]; // Nur eingreifen wenn wir beim ersten Link sind if (activeElement === firstLinkInMenu) { e.preventDefault(); var triggerElement = megaMenu.previousElementSibling; triggerElement.focus(); return; } } } // Ansonsten: Normale Tab-Navigation nicht unterbrechen! } }); }; MegaMenuRenderer.prototype.openMegaMenu = function (trigger, menu) { trigger.setAttribute('aria-expanded', 'true'); menu.setAttribute('aria-expanded', 'true'); menu.classList.add('js-open'); console.log('Menu geöffnet:', trigger.textContent); }; MegaMenuRenderer.prototype.closeMegaMenu = function (trigger, menu) { trigger.setAttribute('aria-expanded', 'false'); menu.setAttribute('aria-expanded', 'false'); menu.classList.remove('js-open'); console.log('Menu geschlossen:', trigger.textContent); }; MegaMenuRenderer.prototype.toggleMegaMenu = function (trigger, menu) { var isOpen = trigger.getAttribute('aria-expanded') === 'true'; if (isOpen) { this.closeMegaMenu(trigger, menu); } else { this.closeAllMegaMenus(); this.openMegaMenu(trigger, menu); } }; MegaMenuRenderer.prototype.closeAllMegaMenus = function () { var allTriggers = document.querySelectorAll('#Mega-Menu > ul > li > a[aria-expanded="true"], #Mega-Menu > ul > li > span[aria-expanded="true"]'); for (var i = 0; i < allTriggers.length; i++) { var trigger = allTriggers[i]; var menu = trigger.nextElementSibling; if (menu) { this.closeMegaMenu(trigger, menu); } } }; // removed unused closeOtherMegaMenus (was previously declared but not used) MegaMenuRenderer.prototype.focusFirstLink = function (megaMenu) { var firstLink = megaMenu.querySelector('a'); if (firstLink) { firstLink.focus(); } }; MegaMenuRenderer.prototype.createScreenReaderAnnouncer = function () { // Screenreader-Ankündigungen var srAnnouncer = document.createElement('div'); srAnnouncer.setAttribute('aria-live', 'polite'); srAnnouncer.setAttribute('aria-atomic', 'true'); srAnnouncer.className = 'sr-only'; document.body.appendChild(srAnnouncer); console.log('Screenreader-Ankündigungen sind jetzt bereit'); }; return MegaMenuRenderer; }()); export { MegaMenuRenderer }; //# sourceMappingURL=MegaMenuRenderer.js.map