- Deleted obsolete localization file for English (en-us). - Updated version number from 1.0.4 to 1.1.0 in manifests.js and manifests.json. - Added localization support for German (de-de) in a new localization file. - Removed unnecessary dependencies from manifests. - Added .gitignore to exclude build artifacts and temporary files.
471 lines
20 KiB
JavaScript
471 lines
20 KiB
JavaScript
// 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
|