Skip to content

Commit

Permalink
Merge pull request #979 from helsingborg-stad/feat/handle-mega-menu-t…
Browse files Browse the repository at this point in the history
…rigger-separately

feat: handle mega menu triggering separately
  • Loading branch information
NiclasNorin authored Nov 25, 2024
2 parents 9b3369b + bc80de5 commit 4fb4730
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
2 changes: 2 additions & 0 deletions source/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {initializeModal} from './modal';
import {initializeIframeAcceptance} from './iframeAcceptance';
import {initializeSelectFilter} from './selectFilter';
import {initializeSelectSort} from './selectSort';
import {initializeMegaMenus} from './megaMenu';

// Instances
const DeviceDetectInstance = new DeviceDetect();
Expand Down Expand Up @@ -112,6 +113,7 @@ document.addEventListener('DOMContentLoaded', () => {
AnchorMenu();
initializeSelectFilter();
initializeSelectSort();
initializeMegaMenus();

// Utility functions
moveElements(moveElement);
Expand Down
97 changes: 97 additions & 0 deletions source/js/megaMenu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
class MegaMenu {
private isOpen = false;

constructor(private megaMenu: Element, private triggers: NodeListOf<Element>) {
this.setupListeners();
}

/**
* Sets up event listeners for the mega menu.
*/
private setupListeners() {
this.triggers.forEach(trigger => {
trigger.addEventListener('click', () => {
this.triggerClickHandler(trigger);
});
});

document.addEventListener('click', (e) => {
this.handleClickOutside(e as PointerEvent);
});

document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
this.close();
}
});
}

/**
* Handles the click event on the trigger element.
* Toggles the visibility of the mega menu and updates the aria-hidden attribute accordingly.
* Also updates the state of the triggers based on the visibility of the mega menu.
*
* @param trigger - The trigger element that was clicked.
*/
private triggerClickHandler(trigger: Element) {
this.megaMenu.classList.toggle('u-display--none');
this.isOpen = this.megaMenu.classList.contains('u-display--none') ? false : true;

this.megaMenu.setAttribute('aria-hidden', this.isOpen.toString());

this.changeTriggersStates();
}

/**
* Changes the states of the triggers based on the specified isOpen value.
*/
private changeTriggersStates() {
this.triggers.forEach(trigger => {
trigger.setAttribute('aria-pressed', this.isOpen.toString());
});
}

/**
* Handles the click event outside of the mega menu.
* If the click is not inside the menu or on any of the triggers, it closes the menu.
* @param event - The pointer event object.
*/
private handleClickOutside(event: PointerEvent) {
if (!this.isOpen) return;
const target = event.target as Element;

const isClickInsideMenu = this.megaMenu.contains(target);
const isClickOnTrigger = Array.from(this.triggers).some(trigger => trigger.contains(target));

if (!isClickInsideMenu && !isClickOnTrigger) {
this.close();
}
}

/**
* Closes the mega menu.
*
* This method adds the 'u-display--none' class to the mega menu element, sets the 'aria-hidden' attribute to 'true',
* and changes the triggers' states to false.
*/
private close() {
if (!this.isOpen) return;
this.isOpen = false;
this.megaMenu.classList.add('u-display--none');
this.megaMenu.setAttribute('aria-hidden', 'true');
this.changeTriggersStates();
}
}

export function initializeMegaMenus() {
document.querySelectorAll('.c-megamenu').forEach(megaMenu => {
const id = megaMenu.id;
const triggers = document.querySelectorAll(`[data-js-mega-menu-trigger="${id}"]`);

if (triggers.length <= 0) {
return;
}

new MegaMenu(megaMenu, triggers);
});
}

0 comments on commit 4fb4730

Please sign in to comment.