233 lines
8.5 KiB
JavaScript
233 lines
8.5 KiB
JavaScript
/*!
|
|
* metismenujs - v1.4.0
|
|
* MetisMenu: Collapsible menu plugin with Vanilla-JS
|
|
* https://github.com/onokumus/metismenujs#readme
|
|
*
|
|
* Made by Osman Nuri Okumus <onokumus@gmail.com> (https://github.com/onokumus)
|
|
* Under MIT License
|
|
*/
|
|
(function (global, factory) {
|
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
typeof define === 'function' && define.amd ? define(factory) :
|
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.MetisMenu = factory());
|
|
})(this, (function () { 'use strict';
|
|
|
|
const Default = {
|
|
parentTrigger: 'li',
|
|
subMenu: 'ul',
|
|
toggle: true,
|
|
triggerElement: 'a',
|
|
};
|
|
const ClassName = {
|
|
ACTIVE: 'mm-active',
|
|
COLLAPSE: 'mm-collapse',
|
|
COLLAPSED: 'mm-collapsed',
|
|
COLLAPSING: 'mm-collapsing',
|
|
METIS: 'metismenu',
|
|
SHOW: 'mm-show',
|
|
};
|
|
|
|
/* eslint-disable max-len */
|
|
class MetisMenu {
|
|
/**
|
|
* Creates an instance of MetisMenu.
|
|
*
|
|
* @constructor
|
|
* @param {Element | string} element
|
|
* @param {IMMOptions} [options]
|
|
* @memberof MetisMenu
|
|
*/
|
|
constructor(element, options) {
|
|
this.element = MetisMenu.isElement(element) ? element : document.querySelector(element);
|
|
this.config = Object.assign(Object.assign({}, Default), options);
|
|
this.disposed = false;
|
|
this.triggerArr = [];
|
|
this.boundEventHandler = this.clickEvent.bind(this);
|
|
this.init();
|
|
}
|
|
static attach(el, opt) {
|
|
return new MetisMenu(el, opt);
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
init() {
|
|
const { METIS, ACTIVE, COLLAPSE } = ClassName;
|
|
this.element.classList.add(METIS);
|
|
const uls = [...this.element.querySelectorAll(this.config.subMenu)];
|
|
if (uls.length === 0) {
|
|
return;
|
|
}
|
|
uls.forEach((ul) => {
|
|
ul.classList.add(COLLAPSE);
|
|
const li = ul.closest(this.config.parentTrigger);
|
|
if (li === null || li === void 0 ? void 0 : li.classList.contains(ACTIVE)) {
|
|
this.show(ul);
|
|
}
|
|
else {
|
|
this.hide(ul);
|
|
}
|
|
const a = li === null || li === void 0 ? void 0 : li.querySelector(this.config.triggerElement);
|
|
if ((a === null || a === void 0 ? void 0 : a.getAttribute("aria-disabled")) === "true") {
|
|
return;
|
|
}
|
|
a === null || a === void 0 ? void 0 : a.setAttribute("aria-expanded", "false");
|
|
a === null || a === void 0 ? void 0 : a.addEventListener("click", this.boundEventHandler);
|
|
this.triggerArr.push(a);
|
|
});
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
clickEvent(evt) {
|
|
if (!this.disposed) {
|
|
const target = evt === null || evt === void 0 ? void 0 : evt.currentTarget;
|
|
if (target && target.tagName === "A") {
|
|
evt.preventDefault();
|
|
}
|
|
const li = target.closest(this.config.parentTrigger);
|
|
const ul = li === null || li === void 0 ? void 0 : li.querySelector(this.config.subMenu);
|
|
this.toggle(ul);
|
|
}
|
|
}
|
|
update() {
|
|
this.disposed = false;
|
|
this.init();
|
|
}
|
|
dispose() {
|
|
this.triggerArr.forEach((arr) => {
|
|
arr.removeEventListener("click", this.boundEventHandler);
|
|
});
|
|
this.disposed = true;
|
|
}
|
|
on(evtType, handler, options) {
|
|
this.element.addEventListener(evtType, handler, options);
|
|
return this;
|
|
}
|
|
off(evtType, handler, options) {
|
|
this.element.removeEventListener(evtType, handler, options);
|
|
return this;
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
emit(evtType, evtData, shouldBubble = false) {
|
|
const evt = new CustomEvent(evtType, {
|
|
bubbles: shouldBubble,
|
|
detail: evtData,
|
|
});
|
|
this.element.dispatchEvent(evt);
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
toggle(ul) {
|
|
const li = ul.closest(this.config.parentTrigger);
|
|
if (li === null || li === void 0 ? void 0 : li.classList.contains(ClassName.ACTIVE)) {
|
|
this.hide(ul);
|
|
}
|
|
else {
|
|
this.show(ul);
|
|
}
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
show(el) {
|
|
var _a;
|
|
const ul = el;
|
|
const { ACTIVE, COLLAPSE, COLLAPSED, COLLAPSING, SHOW } = ClassName;
|
|
if (this.isTransitioning || ul.classList.contains(COLLAPSING)) {
|
|
return;
|
|
}
|
|
const complete = () => {
|
|
ul.classList.remove(COLLAPSING);
|
|
ul.style.height = "";
|
|
ul.removeEventListener("transitionend", complete);
|
|
this.setTransitioning(false);
|
|
this.emit("shown.metisMenu", {
|
|
shownElement: ul,
|
|
});
|
|
};
|
|
const li = ul.closest(this.config.parentTrigger);
|
|
li === null || li === void 0 ? void 0 : li.classList.add(ACTIVE);
|
|
const a = li === null || li === void 0 ? void 0 : li.querySelector(this.config.triggerElement);
|
|
a === null || a === void 0 ? void 0 : a.setAttribute("aria-expanded", "true");
|
|
a === null || a === void 0 ? void 0 : a.classList.remove(COLLAPSED);
|
|
ul.style.height = "0px";
|
|
ul.classList.remove(COLLAPSE);
|
|
ul.classList.remove(SHOW);
|
|
ul.classList.add(COLLAPSING);
|
|
const eleParentSiblins = [].slice.call((_a = li === null || li === void 0 ? void 0 : li.parentNode) === null || _a === void 0 ? void 0 : _a.children).filter((c) => c !== li);
|
|
if (this.config.toggle && eleParentSiblins.length > 0) {
|
|
eleParentSiblins.forEach((sibli) => {
|
|
const sibUl = sibli.querySelector(this.config.subMenu);
|
|
if (sibUl) {
|
|
this.hide(sibUl);
|
|
}
|
|
});
|
|
}
|
|
this.setTransitioning(true);
|
|
ul.classList.add(COLLAPSE);
|
|
ul.classList.add(SHOW);
|
|
ul.style.height = `${ul.scrollHeight}px`;
|
|
this.emit("show.metisMenu", {
|
|
showElement: ul,
|
|
});
|
|
ul.addEventListener("transitionend", complete);
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
hide(el) {
|
|
const { ACTIVE, COLLAPSE, COLLAPSED, COLLAPSING, SHOW } = ClassName;
|
|
const ul = el;
|
|
if (this.isTransitioning || !ul.classList.contains(SHOW)) {
|
|
return;
|
|
}
|
|
this.emit("hide.metisMenu", {
|
|
hideElement: ul,
|
|
});
|
|
const li = ul.closest(this.config.parentTrigger);
|
|
li === null || li === void 0 ? void 0 : li.classList.remove(ACTIVE);
|
|
const complete = () => {
|
|
ul.classList.remove(COLLAPSING);
|
|
ul.classList.add(COLLAPSE);
|
|
ul.style.height = "";
|
|
ul.removeEventListener("transitionend", complete);
|
|
this.setTransitioning(false);
|
|
this.emit("hidden.metisMenu", {
|
|
hiddenElement: ul,
|
|
});
|
|
};
|
|
ul.style.height = `${ul.getBoundingClientRect().height}px`;
|
|
ul.style.height = `${ul.offsetHeight}px`;
|
|
ul.classList.add(COLLAPSING);
|
|
ul.classList.remove(COLLAPSE);
|
|
ul.classList.remove(SHOW);
|
|
this.setTransitioning(true);
|
|
ul.addEventListener("transitionend", complete);
|
|
ul.style.height = "0px";
|
|
const a = li === null || li === void 0 ? void 0 : li.querySelector(this.config.triggerElement);
|
|
a === null || a === void 0 ? void 0 : a.setAttribute("aria-expanded", "false");
|
|
a === null || a === void 0 ? void 0 : a.classList.add(COLLAPSED);
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
setTransitioning(isTransitioning) {
|
|
this.isTransitioning = isTransitioning;
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
static isElement(element) {
|
|
return Boolean(element.classList);
|
|
}
|
|
}
|
|
|
|
return MetisMenu;
|
|
|
|
}));
|
|
//# sourceMappingURL=metismenujs.js.map
|