class Menu {
constructor(domNode, parent) { this.domNode = domNode; this.parent = parent; this.items = []; this.firstItem = null; this.lastItem = null; this.buildItems(); } buildItems() { Array.from(this.domNode.children).forEach((child) => { if (child.children.length > 0) { this.items.push(new SidenavItem(child.querySelector('a.Vlt-sidemenu__trigger, a.Vlt-sidemenu__link, .Vlt-sidemenu__title'), this.parent)); } }); this.firstItem = this.items[0]; this.lastItem = this.items[this.items.length - 1]; } setFocusToNextItem(current) { let node = null; if (current === this.lastItem) { node = this.firstItem.domNode; } else { node = this.items[this.items.indexOf(current) + 1].domNode; } node.tabIndex = 0; node.focus(); } setFocusToPreviousItem(item) { if (item === this.firstItem) { this.lastItem.domNode.focus(); } else { this.items[this.items.indexOf(item) - 1].domNode.focus(); } } setFocusToFirstItem() { this.firstItem.domNode.focus(); }
}
class SidenavItem
{
constructor(domNode, parent) { this.domNode = domNode; this.parent = parent; this.menu = null; this.buildItem(); this.keyCode = Object.freeze({ 'ENTER': 13, 'LEFT': 37, 'UP': 38, 'RIGHT': 39, 'DOWN': 40 }); this.domNode.addEventListener('keydown', this.handleKeyDown.bind(this)); this.domNode.addEventListener('click', this.handleClick.bind(this)); } buildItem() { if (this.isMenu()) { this.menu = new Menu(this.domNode.nextElementSibling, this); } } isMenu() { return this.domNode.nextElementSibling && this.domNode.nextElementSibling.tagName === 'UL'; } handleKeyDown(event) { let target = event.currentTarget; let key = event.key; let bubbleUp = false; switch (event.keyCode) { case this.keyCode.ENTER: if (this.menu) { this.toggleElement(true); bubbleUp = true; } break; case this.keyCode.DOWN: this.parent.setFocusToNextItem(this); bubbleUp = true; break; case this.keyCode.LEFT: this.parent.toggleElement(true); bubbleUp = true; break; case this.keyCode.RIGHT: if (this.menu) { this.toggleElement(true); } bubbleUp = true; break; case this.keyCode.UP: this.parent.setFocusToPreviousItem(this); bubbleUp = true; break; } if (bubbleUp) { event.stopPropagation(); event.preventDefault(); } }; handleClick(event) { if (this.menu) { event.preventDefault(); event.stopPropagation(); this.toggleElement(); } } toggleElement(focus) { if (this.domNode.classList.contains('Vlt-sidemenu__trigger_active')) { this.domNode.setAttribute('aria-expanded', 'false'); this.domNode.classList.remove('Vlt-sidemenu__trigger_active'); this.domNode.tabIndex = -1; if (focus) { this.domNode.focus(); } } else { this.domNode.tabIndex = 0; this.domNode.setAttribute('aria-expanded', 'true'); this.domNode.classList.add('Vlt-sidemenu__trigger_active'); this.domNode.dispatchEvent(new CustomEvent('menuClosed', { bubbles: true })); if (focus) { this.menu.setFocusToFirstItem(); } } } setFocusToPreviousLevel() { if (this.menu) { this.domNode.setAttribute('aria-expanded', 'false'); this.domNode.classList.remove('Vlt-sidemenu__trigger_active'); this.domNode.tabIndex = -1; this.domNode.focus(); } else { this.parent.setFocusToPreviousItem(this); } } setFocusToNextItem(current) { if (this.menu) { this.menu.setFocusToNextItem(current); } } setFocusToPreviousItem(current) { if (this.menu) { this.menu.setFocusToPreviousItem(current); } }
}
export default class Sidenav
{
constructor(domNode) { this.domNode = document.getElementById('sidenav'); this.container = document.getElementById('Vlt-sidenav'); this.collapseTrigger = document.getElementById('Vlt-sidenav-collapse-trigger'); if (this.domNode) { this.buildMenu(); this.setActiveItem(); this.expandActiveMenu(); this.setupListeners(); } } setupListeners() { this.domNode.addEventListener('menuClosed', this.closeOpenedMenu.bind(this)); this.collapseTrigger.addEventListener('click', this.collapseHandler.bind(this)); document.querySelector('body').addEventListener('click', this.closeMobileMenu.bind(this)); document.querySelector('body').addEventListener('touchstart', this.closeMobileMenu.bind(this)); } buildMenu() { this.menu = new Menu(this.domNode.firstElementChild, this); } setFocusToNextItem(current) { this.menu.setFocusToNextItem(current); } setFocusToPreviousItem(current) { this.menu.setFocusToPreviousItem(current); } // No-op toggleElement() {} setActiveItem() { let url = document.querySelector('nav.sidenav').dataset.active; let activeItemSelector = `.Vlt-sidemenu__link[href="${url}"]`; let activeItem = document.querySelector(activeItemSelector); if (activeItem) { activeItem.classList.add('Vlt-sidemenu__link_active'); } } expandActiveMenu() { const activeItem = this.domNode.querySelector('.Vlt-sidemenu__link_active'); if (activeItem) { let activeTrigger = activeItem.closest('ul').previousElementSibling; while (activeTrigger) { activeTrigger.classList.add('Vlt-sidemenu__trigger_active', 'Vlt-sidemenu__trigger_current'); activeTrigger = activeTrigger.parentNode.closest('ul').previousElementSibling; } } } closeOpenedMenu(event) { Array.from(this.domNode.querySelectorAll('.Vlt-sidemenu__trigger_active')).forEach((subMenu) => { if (subMenu !== event.target && !subMenu.parentNode.contains(event.target)) { subMenu.classList.remove('Vlt-sidemenu__trigger_active'); } }); } collapseHandler(event) { event.preventDefault(); event.stopPropagation(); this.container.classList.add('Vlt-sidenav--animate'); if (this.container.classList.contains('Vlt-sidenav_visible')) { this.container.classList.remove('Vlt-sidenav_visible'); } else if (this.container.classList.contains('Vlt-sidenav--collapsed')) { this.container.classList.remove('Vlt-sidenav--collapsed'); } else { this.container.classList.add('Vlt-sidenav--collapsed'); } } closeMobileMenu(event) { if (!this.domNode.contains(event.target)) { this.container.classList.remove('Vlt-sidenav_visible'); document.body.classList.remove('Vlt-body--mobile-menu-open'); } }
}