import isDesktop from '../helper/isDesktop' /** * Calculate nested children height in sidebar menu * @param {HTMLElement} el */ const calculateChildrenHeight = (el, deep = false) => { const children = el.children let height = 0 for(let i = 0; i < el.childElementCount; i++) { const child = children[i] height += child.querySelector('.submenu-link').clientHeight // 2-level menu if(deep && child.classList.contains('has-sub')) { const subsubmenu = child.querySelector('.submenu') if(subsubmenu.classList.contains('submenu-open')) { const childrenHeight = ~~[...subsubmenu.querySelectorAll('.submenu-link')].reduce((acc,curr) => acc + curr.clientHeight,0) height += childrenHeight } } } el.style.setProperty('--submenu-height', height + 'px') return height } /** * a Sidebar component * @param {HTMLElement} el - sidebar element * @param {object} options={} - options */ class Sidebar { constructor(el, options = {}) { this.sidebarEL = el instanceof HTMLElement ? el : document.querySelector(el) this.options = options this.init() } /** * initialize the sidebar */ init() { // add event listener to sidebar document .querySelectorAll(".burger-btn") .forEach((el) => el.addEventListener("click", this.toggle.bind(this))) document .querySelectorAll(".sidebar-hide") .forEach((el) => el.addEventListener("click", this.toggle.bind(this))) window.addEventListener("resize", this.onResize.bind(this)) const toggleSubmenu = (el) => { if (el.classList.contains("submenu-open")) { el.classList.remove('submenu-open') el.classList.add('submenu-closed') } else { el.classList.remove("submenu-closed") el.classList.add("submenu-open") } } let sidebarItems = document.querySelectorAll(".sidebar-item.has-sub") for (var i = 0; i < sidebarItems.length; i++) { let sidebarItem = sidebarItems[i] sidebarItems[i] .querySelector(".sidebar-link") .addEventListener("click", (e) => { e.preventDefault() let submenu = sidebarItem.querySelector(".submenu") toggleSubmenu(submenu) }) // If submenu has submenu const submenuItems = sidebarItem.querySelectorAll('.submenu-item.has-sub') submenuItems.forEach(item => { item.addEventListener('click', () => { const submenuLevelTwo = item.querySelector('.submenu') toggleSubmenu(submenuLevelTwo) // Pass second .submenu const height = calculateChildrenHeight(item.parentElement, true) }) }) } // Perfect Scrollbar Init if (typeof PerfectScrollbar == "function") { const container = document.querySelector(".sidebar-wrapper") const ps = new PerfectScrollbar(container, { wheelPropagation: false, }) } // Scroll into active sidebar setTimeout(() => { this.forceElementVisibility(document.querySelector(".sidebar-item.active")) }, 300) } /** * On Sidebar Rezise Event */ onResize() { if (isDesktop(window)) { this.sidebarEL.classList.add("active") } else { this.sidebarEL.classList.remove("active") } // reset this.deleteBackdrop() this.toggleOverflowBody(true) } /** * Toggle Sidebar */ toggle() { const sidebarState = this.sidebarEL.classList.contains("active") if (sidebarState) { this.hide() } else { this.show() } } /** * Show Sidebar */ show() { this.sidebarEL.classList.add("active") this.sidebarEL.classList.remove("inactive") this.createBackdrop() this.toggleOverflowBody() } /** * Hide Sidebar */ hide() { this.sidebarEL.classList.remove("active") this.sidebarEL.classList.add("inactive") this.deleteBackdrop() this.toggleOverflowBody() } /** * Create Sidebar Backdrop */ createBackdrop() { if (isDesktop(window)) return this.deleteBackdrop() const backdrop = document.createElement("div") backdrop.classList.add("sidebar-backdrop") backdrop.addEventListener("click", this.hide.bind(this)) document.body.appendChild(backdrop) } /** * Delete Sidebar Backdrop */ deleteBackdrop() { const backdrop = document.querySelector(".sidebar-backdrop") if (backdrop) { backdrop.remove() } } /** * Toggle Overflow Body */ toggleOverflowBody(active) { if(isDesktop(window)) return; const sidebarState = this.sidebarEL.classList.contains("active") const body = document.querySelector("body") if (typeof active == "undefined") { body.style.overflowY = sidebarState ? "hidden" : "auto" } else { body.style.overflowY = active ? "auto" : "hidden" } } isElementInViewport(el) { var rect = el.getBoundingClientRect() return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ) } forceElementVisibility(el) { if (!this.isElementInViewport(el)) { el.scrollIntoView(false) } } } let sidebarEl = document.getElementById("sidebar") /** * On First Load */ const onFirstLoad = (sidebarEL) => { if(!sidebarEl) return if (isDesktop(window)) { sidebarEL.classList.add("active") sidebarEL.classList.add('sidebar-desktop') } // Get submenus size let submenus = document.querySelectorAll(".sidebar-item.has-sub .submenu") for (var i = 0; i < submenus.length; i++) { let submenu = submenus[i] const sidebarItem = submenu.parentElement const height = submenu.clientHeight if(sidebarItem.classList.contains('active')) submenu.classList.add('submenu-open') else submenu.classList.add('submenu-closed') setTimeout(() => { const height = calculateChildrenHeight(submenu, true) }, 50); } } if(document.readyState !== 'loading') { onFirstLoad(sidebarEl) } else { window.addEventListener('DOMContentLoaded', () => onFirstLoad(sidebarEl)) } /** * Create Sidebar Wrapper */ if (sidebarEl) { window.sidebar = new Sidebar(sidebarEl) }