UNPKG

@sgalinski/responsive-side-menu

Version:

A simple and extendable slide-in menu

171 lines (156 loc) 5.06 kB
import Polyfills from './polyfills'; import MarkupGenerator from './markupGenerator'; import Settings from './settings'; import PluginRegistry from './pluginRegistry'; import Util from './util'; /** * This is the menus main class */ export default class ResponsiveSideMenu { constructor(_rootElementId, _options = {}) { // load all polyfills Polyfills.load(); Settings.get = _options; this.rootElement = document.getElementById(_rootElementId); this.elements = MarkupGenerator.generateMenuStub(this.rootElement); this.touchStartX = 0; this.touchCurrentX = 0; this.setUpEventListeners(); PluginRegistry.load(); this.elements.menu.setAttribute('inert', true); this.openCurrentPageInMenu(); this.setActiveMenuItem(); let readyEvent = new CustomEvent('rsmReady'); this.rootElement.dispatchEvent(readyEvent); } /** * Set up all major EventListeners */ setUpEventListeners() { Array.from(document.querySelectorAll('a[href="#rsm"]')).forEach(item => { item.addEventListener('click', this.toggle.bind(this)); }); this.elements.menu.addEventListener('click', this.clickOnBackDrop.bind(this)); this.elements.menu.addEventListener('touchstart', this.touchStart.bind(this)); this.elements.menu.addEventListener('touchmove', this.touchMove.bind(this)); this.elements.menu.addEventListener('touchend', this.touchEnd.bind(this)); Array.from(document.querySelectorAll('.rsm-sub-opener')).forEach(item => { item.addEventListener('click', ResponsiveSideMenu.openSubMenu); }); Array.from(document.querySelectorAll('.rsm-back-link a')).forEach(item => { item.addEventListener('click', ResponsiveSideMenu.closeSubMenu); }); this.elements.menu.querySelector('.rsm-close').addEventListener('click', this.close.bind(this)); } /** * Opens the menu */ open() { this.elements.menu.classList.add('rsm-open'); this.elements.menu.removeAttribute('inert'); document.querySelector('body').classList.add('rsm-is-open'); } /** * Closes the menu */ close(event) { if (typeof event !== 'undefined') { event.preventDefault(); } this.elements.menu.classList.remove('rsm-open'); this.elements.menu.setAttribute('inert', true); document.querySelector('body').classList.remove('rsm-is-open'); } /** * Toggles the current menu state (open/close) * * @param event */ toggle(event) { event.preventDefault(); if (this.elements.menu.classList.contains('rsm-open')) { this.close(); } else { this.open(); } } /** * Marks the menu item that points to the current page */ setActiveMenuItem() { let activeLink = this.elements.menu.querySelector(`[href$="${window.location.pathname}"]`); if (activeLink) { activeLink.parentNode.classList.add('rsm-active'); } } /** * Handles clicks on the backdrop * * @param event */ clickOnBackDrop(event) { if (event.target !== this.elements.menu) { return; } this.close(); } touchStart(event) { this.touchStartX = event.touches[0].clientX; } touchMove(event) { if (Settings.get.orientation === 'left') { this.touchCurrentX = Math.max((this.touchStartX - event.touches[0].clientX), 0) * -1; } else { this.touchCurrentX = Math.min(this.touchStartX - event.touches[0].clientX, 0) * -1; } this.elements.slideMenu.style.transition = 'none'; this.elements.slideMenu.style.transform = `translateX(${this.touchCurrentX}px)`; } touchEnd() { this.elements.slideMenu.style.transition = ''; if (Settings.get.orientation === 'left') { if (this.touchCurrentX < this.elements.slideMenu.getBoundingClientRect().width * -0.4) { this.close(); } } else if (this.touchCurrentX > this.elements.slideMenu.getBoundingClientRect().width * 0.4) { this.close(); } this.elements.slideMenu.style.transform = ''; } openCurrentPageInMenu() { let currentPage = window.location.pathname, targetLink = this.elements.menu.querySelector(`a[data-href="${currentPage}"],a[href="${currentPage}"]`); // abort if we are on the homepage if (currentPage !== Settings.get.homepage && targetLink) { let subOpener; if (targetLink.classList.contains('rsm-sub-opener')) { subOpener = targetLink; } else { // take parent instead subOpener = targetLink.parentNode.parentNode.querySelector('.rsm-back-link > a'); } if (!subOpener) { return; } ResponsiveSideMenu.openSubMenu({ // eslint-disable-next-line no-empty-function preventDefault: () => {}, currentTarget: subOpener }); } } static openSubMenu(event) { event.preventDefault(); let subMenuId = event.currentTarget.getAttribute('href'); let panel = document.getElementById(subMenuId.replace('#', '')); if (panel) { panel.classList.add('rsm-sub-open'); Util.parents(panel, '.rsm-root[id]').forEach(parent => parent.classList.add('rsm-sub-open')); } } static closeSubMenu(event) { event.preventDefault(); let subMenuId = event.currentTarget.getAttribute('href'); document.getElementById(subMenuId.replace('#', '')).classList.remove('rsm-sub-open'); } }