// tslint:disable:max-line-length // tslint:disable:match-default-export-name // tslint:disable:typedef // tslint:disable:variable-name import { debugLog } from '../../services/MegaMenuDebug'; var LOG_SOURCE = 'MegaMenuRenderer'; var MegaMenuRenderer = (function () { function MegaMenuRenderer(menuItems, debug) { if (debug === void 0) { debug = false; } this.menuItems = menuItems; this.debug = debug; } MegaMenuRenderer.prototype.render = function (container) { var _this = this; this.dispose(); this._container = container; container.innerHTML = ''; container.id = 'CustomNavigation'; var nav = document.createElement('nav'); nav.id = 'Mega-Menu'; nav.className = 'mega-menu-main'; nav.setAttribute('aria-label', 'Hauptnavigation'); var topLevelUl = document.createElement('ul'); topLevelUl.className = 'mega-menu-top-level'; this.menuItems.forEach(function (topLevelItem) { var topLevelLi = _this.createTopLevelItem(topLevelItem); topLevelUl.appendChild(topLevelLi); }); nav.appendChild(topLevelUl); container.appendChild(nav); this.attachEventListeners(); this.createScreenReaderAnnouncer(); }; MegaMenuRenderer.prototype.dispose = function () { if (this._documentKeydownHandler) { document.removeEventListener('keydown', this._documentKeydownHandler); this._documentKeydownHandler = undefined; } if (this._documentClickHandler) { document.removeEventListener('click', this._documentClickHandler); this._documentClickHandler = undefined; } if (this._announcer && this._announcer.parentElement) { this._announcer.parentElement.removeChild(this._announcer); } this._announcer = undefined; if (this._container) { this._container.innerHTML = ''; } this._container = undefined; }; MegaMenuRenderer.prototype.createTopLevelItem = function (item) { var li = document.createElement('li'); var hasChildren = item.hasChildren() && item.items && item.items.length > 0; var isActive = this.isItemActive(item); var isCurrent = this.isCurrentUrl(item.url); li.className = this.joinClasses('mega-menu-top-item', hasChildren ? 'has-children' : undefined, isActive ? 'is-active' : undefined, isCurrent ? 'is-current' : undefined); var topElement = this.createTopLevelElement(item, hasChildren, isActive, isCurrent); li.appendChild(topElement); if (hasChildren) { var megaMenu = this.createMegaMenu(item); li.appendChild(megaMenu); } return li; }; MegaMenuRenderer.prototype.createTopLevelElement = function (item, hasChildren, isActive, isCurrent) { var element; if (item.url) { element = document.createElement('a'); element.href = item.url; element.className = 'menu-item-link'; } else { element = document.createElement('span'); element.className = 'menu-item-text'; element.setAttribute('tabindex', '0'); } element.textContent = item.label; element.className = this.joinClasses(element.className, hasChildren ? 'menu-item-has-children' : undefined, isActive ? 'is-active' : undefined, isCurrent ? 'is-current' : undefined); if (hasChildren) { element.setAttribute('aria-haspopup', 'true'); element.setAttribute('aria-expanded', 'false'); } if (isCurrent && item.url) { element.setAttribute('aria-current', 'page'); } 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('aria-expanded', 'false'); megaMenuDiv.setAttribute('aria-label', parentItem.label + ' Unterkategorien'); var gridDiv = document.createElement('div'); gridDiv.className = 'mega-menu-grid'; gridDiv.style.setProperty('--megaMenuColumnCount', this.getColumnCount(parentItem.items)); 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 _this = this; var categoryDiv = document.createElement('div'); var isActive = this.isItemActive(item); var isCurrent = this.isCurrentUrl(item.url); categoryDiv.className = this.joinClasses('mega-menu-category', isActive ? 'is-active' : undefined, isCurrent ? 'is-current' : undefined); var h3 = document.createElement('h3'); h3.className = 'mega-menu-category-title'; if (item.url) { var link = document.createElement('a'); link.href = item.url; link.textContent = item.label; link.className = this.joinClasses(isActive ? 'is-active' : undefined, isCurrent ? 'is-current' : undefined); if (isCurrent) { link.setAttribute('aria-current', 'page'); } if (item.hoverText) { link.title = item.hoverText; } h3.appendChild(link); } else { var span = document.createElement('span'); span.textContent = item.label; span.className = this.joinClasses(isActive ? 'is-active' : undefined); if (item.hoverText) { span.title = item.hoverText; } h3.appendChild(span); } categoryDiv.appendChild(h3); if (item.hasChildren() && item.items && item.items.length > 0) { var ul_1 = document.createElement('ul'); ul_1.className = 'mega-menu-links'; item.items.forEach(function (thirdLevelItem) { var li = document.createElement('li'); var link = document.createElement('a'); var isLinkActive = _this.isItemActive(thirdLevelItem); var isLinkCurrent = _this.isCurrentUrl(thirdLevelItem.url); li.className = _this.joinClasses(isLinkActive ? 'is-active' : undefined, isLinkCurrent ? 'is-current' : undefined); link.href = thirdLevelItem.url || '#'; link.textContent = thirdLevelItem.label; link.className = _this.joinClasses(isLinkActive ? 'is-active' : undefined, isLinkCurrent ? 'is-current' : undefined); if (isLinkCurrent && thirdLevelItem.url) { link.setAttribute('aria-current', 'page'); } 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 > .menu-item-text'); 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); } } this.attachGlobalKeyboardNavigation(); this.attachGlobalClickOutsideHandler(); }; MegaMenuRenderer.prototype.attachKeyboardNavigation = function (heading, megaMenu) { var _this = this; heading.addEventListener('keydown', function (e) { if (e.key === 'Enter') { if (heading.tagName === 'A') { return; } e.preventDefault(); _this.toggleMegaMenu(heading, megaMenu); } else if (e.key === ' ') { e.preventDefault(); _this.toggleMegaMenu(heading, megaMenu); } else if (e.key === 'ArrowDown') { e.preventDefault(); _this.openMegaMenu(heading, megaMenu); _this.focusFirstLink(megaMenu); } else if (e.key === 'ArrowUp') { e.preventDefault(); _this.closeMegaMenu(heading, megaMenu); } else if (e.key === 'Escape') { e.preventDefault(); _this.closeMegaMenu(heading, megaMenu); heading.focus(); } }); if (heading.tagName === 'A') { heading.addEventListener('click', function () { debugLog(_this.debug, LOG_SOURCE, 'Link clicked:', heading.href); }); } heading.addEventListener('focus', function () { debugLog(_this.debug, LOG_SOURCE, 'Focus moved to:', heading.textContent); }); }; MegaMenuRenderer.prototype.attachMouseEvents = function (heading, megaMenu) { var _this = this; var parentLi = heading.parentElement; var openTimeout; var closeTimeout; var clearOpenTimeout = function () { if (typeof openTimeout === 'number') { window.clearTimeout(openTimeout); openTimeout = undefined; } }; var clearCloseTimeout = function () { if (typeof closeTimeout === 'number') { window.clearTimeout(closeTimeout); closeTimeout = undefined; } }; parentLi.addEventListener('mouseenter', function () { clearCloseTimeout(); if (megaMenu.classList.contains('js-open')) { return; } clearOpenTimeout(); openTimeout = window.setTimeout(function () { _this.openMegaMenu(heading, megaMenu); openTimeout = undefined; }, MegaMenuRenderer.hoverOpenDelayMs); }); parentLi.addEventListener('mouseleave', function () { clearOpenTimeout(); clearCloseTimeout(); closeTimeout = window.setTimeout(function () { _this.closeMegaMenu(heading, megaMenu); closeTimeout = undefined; }, MegaMenuRenderer.hoverCloseDelayMs); }); heading.addEventListener('focus', function () { clearCloseTimeout(); }); megaMenu.addEventListener('mouseenter', function () { clearCloseTimeout(); }); }; MegaMenuRenderer.prototype.attachFocusManagement = function (heading, megaMenu) { var _this = this; megaMenu.addEventListener('focusout', function () { 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'); if (!isInsideThisMenu && !isOnThisTrigger && !isInAnyMegaMenu && !isOnAnyTopLevel) { debugLog(_this.debug, LOG_SOURCE, 'Closing menu because focus left the navigation.'); _this.closeMegaMenu(heading, megaMenu); } }, 150); }); }; MegaMenuRenderer.prototype.attachGlobalKeyboardNavigation = function () { var _this = this; this._documentKeydownHandler = 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(); } } if (e.key === 'Tab') { 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) { e.preventDefault(); var firstLink = megaMenu.querySelector('a'); if (firstLink) { firstLink.focus(); } return; } } } 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]; if (activeElement === firstLinkInMenu) { e.preventDefault(); var triggerElement = megaMenu.previousElementSibling; triggerElement.focus(); return; } } } } }; document.addEventListener('keydown', this._documentKeydownHandler); }; MegaMenuRenderer.prototype.attachGlobalClickOutsideHandler = function () { var _this = this; this._documentClickHandler = function (e) { var eventTarget = e.target; var isInsideNavigation = !!(eventTarget && typeof eventTarget.closest === 'function' && eventTarget.closest('#CustomNavigation')); if (isInsideNavigation) { return; } var openMenu = document.querySelector('.mega-menu.js-open'); if (openMenu) { debugLog(_this.debug, LOG_SOURCE, 'Closing menu because of outside click.'); _this.closeAllMegaMenus(); } }; document.addEventListener('click', this._documentClickHandler); }; MegaMenuRenderer.prototype.openMegaMenu = function (trigger, menu) { this.closeAllMegaMenus(menu); trigger.setAttribute('aria-expanded', 'true'); menu.setAttribute('aria-expanded', 'true'); menu.classList.add('js-open'); this.announce((trigger.textContent || '') + ' geƶffnet'); debugLog(this.debug, LOG_SOURCE, 'Menu opened:', trigger.textContent); }; MegaMenuRenderer.prototype.closeMegaMenu = function (trigger, menu) { trigger.setAttribute('aria-expanded', 'false'); menu.setAttribute('aria-expanded', 'false'); menu.classList.remove('js-open'); this.announce((trigger.textContent || '') + ' geschlossen'); debugLog(this.debug, LOG_SOURCE, 'Menu closed:', trigger.textContent); }; MegaMenuRenderer.prototype.toggleMegaMenu = function (trigger, menu) { var isOpen = trigger.getAttribute('aria-expanded') === 'true'; if (isOpen) { this.closeMegaMenu(trigger, menu); } else { this.openMegaMenu(trigger, menu); } }; MegaMenuRenderer.prototype.closeAllMegaMenus = function (exceptMenu) { 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 && menu !== exceptMenu) { this.closeMegaMenu(trigger, menu); } } }; MegaMenuRenderer.prototype.focusFirstLink = function (megaMenu) { var firstLink = megaMenu.querySelector('a'); if (firstLink) { firstLink.focus(); } }; MegaMenuRenderer.prototype.isItemActive = function (item) { if (this.isCurrentUrl(item.url)) { return true; } if (!item.items || item.items.length === 0) { return false; } for (var i = 0; i < item.items.length; i++) { if (this.isItemActive(item.items[i])) { return true; } } return false; }; MegaMenuRenderer.prototype.isCurrentUrl = function (url) { var currentPath = this.normalizePath(window.location.href); var itemPath = this.normalizePath(url); if (!currentPath || !itemPath) { return false; } if (itemPath === '/') { return currentPath === '/'; } if (currentPath === itemPath) { return true; } return currentPath.indexOf(itemPath + '/') === 0; }; MegaMenuRenderer.prototype.normalizePath = function (url) { if (!url) { return ''; } try { var parsedUrl = new URL(url, window.location.origin); var pathname = (parsedUrl.pathname || '/').replace(/\/+$/, ''); return (pathname || '/').toLowerCase(); } catch (error) { return ''; } }; MegaMenuRenderer.prototype.getColumnCount = function (items) { var itemCount = items ? items.length : 0; var columnCount = Math.min(Math.max(itemCount, 1), 4); return columnCount.toString(); }; MegaMenuRenderer.prototype.joinClasses = function () { var classNames = []; for (var _i = 0; _i < arguments.length; _i++) { classNames[_i] = arguments[_i]; } var filteredClassNames = []; for (var i = 0; i < classNames.length; i++) { if (classNames[i]) { filteredClassNames.push(classNames[i]); } } return filteredClassNames.join(' '); }; MegaMenuRenderer.prototype.createScreenReaderAnnouncer = function () { var existingAnnouncer = document.getElementById('mega-menu-sr-announcer'); if (existingAnnouncer) { this._announcer = existingAnnouncer; return; } var srAnnouncer = document.createElement('div'); srAnnouncer.id = 'mega-menu-sr-announcer'; srAnnouncer.setAttribute('aria-live', 'polite'); srAnnouncer.setAttribute('aria-atomic', 'true'); srAnnouncer.className = 'sr-only'; document.body.appendChild(srAnnouncer); this._announcer = srAnnouncer; debugLog(this.debug, LOG_SOURCE, 'Screen reader announcer is ready.'); }; MegaMenuRenderer.prototype.announce = function (message) { var _this = this; if (this._announcer) { this._announcer.textContent = ''; window.setTimeout(function () { if (_this._announcer) { _this._announcer.textContent = message; } }, 20); } }; MegaMenuRenderer.hoverOpenDelayMs = 140; MegaMenuRenderer.hoverCloseDelayMs = 180; return MegaMenuRenderer; }()); export { MegaMenuRenderer }; //# sourceMappingURL=MegaMenuRenderer.js.map