@sgalinski/responsive-side-menu
Version:
A simple and extendable slide-in menu
171 lines (156 loc) • 5.06 kB
JavaScript
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');
}
}