Update MegaMenu Application Customizer: Version bump to 1.1.0, localization support added, and unnecessary files removed
- 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.
This commit is contained in:
@@ -13,25 +13,29 @@ const LOG_SOURCE: string = 'MegaMenuRenderer';
|
||||
export class MegaMenuRenderer {
|
||||
private static readonly hoverOpenDelayMs: number = 140;
|
||||
private static readonly hoverCloseDelayMs: number = 180;
|
||||
private _container: HTMLElement | undefined;
|
||||
private _documentKeydownHandler: ((event: KeyboardEvent) => void) | undefined;
|
||||
private _documentClickHandler: ((event: MouseEvent) => void) | undefined;
|
||||
private _announcer: HTMLElement | undefined;
|
||||
|
||||
constructor(
|
||||
private menuItems: IMenuItem[],
|
||||
private debug: boolean = false
|
||||
) { }
|
||||
|
||||
public render(container: HTMLElement) {
|
||||
public render(container: HTMLElement): void {
|
||||
this.dispose();
|
||||
this._container = container;
|
||||
container.innerHTML = '';
|
||||
container.id = 'CustomNavigation';
|
||||
|
||||
const nav = document.createElement('nav');
|
||||
nav.id = 'Mega-Menu';
|
||||
nav.className = 'mega-menu-main';
|
||||
nav.setAttribute('role', 'navigation');
|
||||
nav.setAttribute('aria-label', 'Hauptnavigation');
|
||||
|
||||
const topLevelUl = document.createElement('ul');
|
||||
topLevelUl.className = 'mega-menu-top-level';
|
||||
topLevelUl.setAttribute('role', 'menubar');
|
||||
|
||||
this.menuItems.forEach(topLevelItem => {
|
||||
const topLevelLi = this.createTopLevelItem(topLevelItem);
|
||||
@@ -45,6 +49,25 @@ export class MegaMenuRenderer {
|
||||
this.createScreenReaderAnnouncer();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
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;
|
||||
}
|
||||
|
||||
private createTopLevelItem(item: IMenuItem): HTMLLIElement {
|
||||
const li = document.createElement('li');
|
||||
const hasChildren = item.hasChildren() && item.items && item.items.length > 0;
|
||||
@@ -57,7 +80,6 @@ export class MegaMenuRenderer {
|
||||
isActive ? 'is-active' : undefined,
|
||||
isCurrent ? 'is-current' : undefined
|
||||
);
|
||||
li.setAttribute('role', 'none');
|
||||
|
||||
const topElement = this.createTopLevelElement(item, hasChildren, isActive, isCurrent);
|
||||
li.appendChild(topElement);
|
||||
@@ -83,7 +105,6 @@ export class MegaMenuRenderer {
|
||||
element.setAttribute('tabindex', '0');
|
||||
}
|
||||
|
||||
element.setAttribute('role', 'menuitem');
|
||||
element.textContent = item.label;
|
||||
element.className = this.joinClasses(
|
||||
element.className,
|
||||
@@ -111,7 +132,6 @@ export class MegaMenuRenderer {
|
||||
private createMegaMenu(parentItem: IMenuItem): HTMLDivElement {
|
||||
const megaMenuDiv = document.createElement('div');
|
||||
megaMenuDiv.className = 'mega-menu';
|
||||
megaMenuDiv.setAttribute('role', 'menu');
|
||||
megaMenuDiv.setAttribute('aria-expanded', 'false');
|
||||
megaMenuDiv.setAttribute('aria-label', parentItem.label + ' Unterkategorien');
|
||||
|
||||
@@ -213,7 +233,7 @@ export class MegaMenuRenderer {
|
||||
}
|
||||
|
||||
private attachEventListeners(): void {
|
||||
const headings = document.querySelectorAll('#Mega-Menu > ul > li > a, #Mega-Menu > ul > li > span[role="menuitem"]');
|
||||
const headings = document.querySelectorAll('#Mega-Menu > ul > li > a, #Mega-Menu > ul > li > .menu-item-text');
|
||||
|
||||
for (let i = 0; i < headings.length; i++) {
|
||||
const heading = headings[i] as HTMLElement;
|
||||
@@ -336,7 +356,7 @@ export class MegaMenuRenderer {
|
||||
}
|
||||
|
||||
private attachGlobalKeyboardNavigation(): void {
|
||||
document.addEventListener('keydown', (e: KeyboardEvent) => {
|
||||
this._documentKeydownHandler = (e: KeyboardEvent): void => {
|
||||
const activeElement = document.activeElement as HTMLElement;
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
@@ -380,11 +400,12 @@ export class MegaMenuRenderer {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
document.addEventListener('keydown', this._documentKeydownHandler);
|
||||
}
|
||||
|
||||
private attachGlobalClickOutsideHandler(): void {
|
||||
document.addEventListener('click', (e: MouseEvent) => {
|
||||
this._documentClickHandler = (e: MouseEvent): void => {
|
||||
const eventTarget = e.target as Element;
|
||||
const isInsideNavigation = !!(eventTarget && typeof eventTarget.closest === 'function' &&
|
||||
eventTarget.closest('#CustomNavigation'));
|
||||
@@ -399,7 +420,8 @@ export class MegaMenuRenderer {
|
||||
debugLog(this.debug, LOG_SOURCE, 'Closing menu because of outside click.');
|
||||
this.closeAllMegaMenus();
|
||||
}
|
||||
});
|
||||
};
|
||||
document.addEventListener('click', this._documentClickHandler);
|
||||
}
|
||||
|
||||
private openMegaMenu(trigger: HTMLElement, menu: HTMLElement): void {
|
||||
@@ -407,6 +429,7 @@ export class MegaMenuRenderer {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -414,6 +437,7 @@ export class MegaMenuRenderer {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -514,7 +538,9 @@ export class MegaMenuRenderer {
|
||||
}
|
||||
|
||||
private createScreenReaderAnnouncer(): void {
|
||||
if (document.getElementById('mega-menu-sr-announcer')) {
|
||||
const existingAnnouncer: HTMLElement = document.getElementById('mega-menu-sr-announcer');
|
||||
if (existingAnnouncer) {
|
||||
this._announcer = existingAnnouncer;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -524,7 +550,19 @@ export class MegaMenuRenderer {
|
||||
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.');
|
||||
}
|
||||
|
||||
private announce(message: string): void {
|
||||
if (this._announcer) {
|
||||
this._announcer.textContent = '';
|
||||
window.setTimeout((): void => {
|
||||
if (this._announcer) {
|
||||
this._announcer.textContent = message;
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user