import {Component, Element, h, State, Listen, Prop} from '@stencil/core'; import {MenuType, SiteLogoSize, ColorVariant} from '../../../utils/utils'; @Component({ tag: 'hy-menu-sidebar', styleUrl: 'menu-sidebar.scss', shadow: false, }) export class MenuSidebar { @Element() el: HTMLElement; /** * Is menu open boolean. */ @Prop() menuIsOpen = false; /** * Menu type. Defaults to sidenav. */ @Prop() menuType: MenuType = MenuType.sidenav; /** * Upper menus panel boolean. */ @Prop() panelOpen = false; /** * Label for panel toggle button. */ @Prop() panelToggleLabel?: string; /** * Label for panel toggle button. */ @Prop() panelToggleAriaLabel?: string; /** * Label for panel toggle button. */ @Prop() panelToggleCloseLabel?: string; /** * Label for panel toggle button. */ @Prop() panelToggleCloseAriaLabel?: string; /** * Url for logo. */ @Prop() logoUrl?: string; /** * Logo label */ @Prop() logoLabel?: string; /** * Logo size */ @Prop() size: SiteLogoSize = SiteLogoSize.big; /** * Isdemo */ @Prop() isDemo: boolean = false; /** * Previous panel to be toggled to keep track. */ @Prop({mutable: true}) minHeight: any = null; @State() previousPanel: HTMLElement; /** * Previous item to be toggle to keep track. */ @State() previousItem; //@State() previousItem: HTMLElement; @Listen('panelToggle') panelToggle(e) { if (this.menuIsOpen) { this.closeWholeTree(); const body = document.querySelector('body') as HTMLElement; this.panelOpen = !this.panelOpen; this.panelOpen ? body.classList.add('hy-menu-sidepanel__no-scroll') : body.classList.remove('hy-menu-sidepanel__no-scroll'); } else { this.panelOpen = !this.panelOpen; const body = document.querySelector('body') as HTMLElement; this.panelOpen ? body.classList.add('hy-menu-sidepanel__no-scroll') : body.classList.remove('hy-menu-sidepanel__no-scroll'); } e.preventDefault(); e.stopPropagation(); } componentWillLoad() { const sideBar = document.querySelector('.layout-sidebar-first'); if (sideBar) { if (this.menuIsOpen) { sideBar.classList.add('menu-is-open'); } else if (sideBar.classList.contains('menu-is-open')) { sideBar.classList.remove('menu-is-open'); } } } componentDidLoad() { const sidebarContainer = document.querySelector('.hy-menu-sidebar--container'); if (sidebarContainer) { sidebarContainer.addEventListener('click', (e) => { if ((e.target as HTMLElement).classList.contains('sidebar-open')) { this.closeWholeTree(); } }); } const sidepanelContainer = document.querySelector('.hy-menu-sidepanel--container'); if (sidepanelContainer) { sidepanelContainer.addEventListener('click', (e) => { if ((e.target as HTMLElement).classList.contains('sidepanel-open')) { this.panelToggle(e); } }); } const panels = document.querySelectorAll('.hy-menu-level-container'); if (panels) { const height = Math.max.apply( Math, [...panels].map((panel) => panel.scrollHeight) ); this.minHeight = height; } } componentWillUpdate() { const sideBar = document.querySelector('.layout-sidebar-first'); if (this.menuIsOpen) { sideBar.classList.add('menu-is-open'); } else if (sideBar.classList.contains('menu-is-open')) { sideBar.classList.remove('menu-is-open'); } } @Listen('click') handlePanel(e) { const target = e.target; const targetElement = target.tagName.toLowerCase(); const possibleTags = [targetElement].some((r) => ['svg', 'path'].indexOf(r) >= 0); if (target.classList.contains('hy-menu-item__label__icon') || possibleTags) { const menuItem = target.closest('.hy-menu-item-sidebar'); const menuItemId = menuItem.getAttribute('data-link-id'); const menuItemLevel = menuItem.getAttribute('item-level'); const menuPanel = menuItem.closest('.hy-menu-level-container'); const childList = menuItem.querySelector('.hy-menu-level-container'); // If no previous, as in first time clicking menu if (!this.previousPanel) { this.updatePrevious(menuPanel, menuItem); this.togglePanel(menuItem, childList); return; } const prevId = this.previousItem.dataset.linkId; const prevLevel = this.previousItem.getAttribute('item-level'); // If clicking same menu item again. if (menuItemId === prevId) { if (menuItem.classList.contains('is-active-item')) { this.togglePanel(menuItem, childList, true); if (menuItemLevel == 1) { this.menuIsOpen = false; } } else { this.togglePanel(menuItem, childList); } return; } // If clicking menu item on same level or going back up if (menuItemLevel <= prevLevel) { // If clicking already active, close children if (menuItem.classList.contains('is-active-item')) { this.togglePanel(menuItem, childList, true); this.updatePrevious(menuPanel, menuItem); return; } // Close previous panels this.togglePanel(this.previousItem, menuPanel, true); // Open newly clicked panel this.togglePanel(menuItem, childList); // Update previous this.updatePrevious(menuPanel, menuItem); } else if (menuItemLevel > prevLevel) { this.togglePanel(menuItem, childList); this.updatePrevious(menuPanel, menuItem); } } e.stopImmediatePropagation(); e.stopPropagation(); } updatePrevious(panel, item) { this.previousPanel = panel; this.previousItem = item; } closeWholeTree() { const sidebarLevel1 = document.querySelector('.hy-menu-level-container--level-1') as HTMLElement; const firstItem = document.querySelector( '.hy-menu-item-sidebar--sidenav.hy-menu-item--level-1.is-active-item' ) as HTMLElement; this.togglePanel(firstItem, sidebarLevel1, true); const body = document.querySelector('body') as HTMLElement; const container = document.querySelector('.hy-menu-sidebar--container') as HTMLElement; if (container.classList.contains('sidebar-open') && firstItem) { container.classList.remove('sidebar-open'); body.classList.remove('hy-menu-sidebar__no-scroll'); } } togglePanel(menuItem, panel, shouldClose = false) { const buttonElement = menuItem.querySelector('button'); const level = menuItem.getAttribute('item-level'); if (level == 1 && !this.menuIsOpen) { if (window.pageYOffset > 50 && !this.isDemo) { window.scrollTo({top: 0}); } this.menuIsOpen = true; } if (shouldClose) { const children = panel.querySelectorAll('.hy-menu-item-sidebar'); const activeChild = [...children].filter((c) => c.classList.contains('is-active-item')); if (level == 1 && activeChild) { this.menuIsOpen = false; } if (activeChild.length > 0) { for (let i = 0; i < activeChild.length; i++) { const element = activeChild[i]; this.closeChildren(element); } } buttonElement.setAttribute('aria-expanded', 'false'); this.closeChildren(menuItem); //this.menuIsOpen = level == 1 ? true : false; return; } buttonElement.setAttribute('aria-expanded', 'true'); panel.classList.add('is-open'); menuItem.classList.add('is-active-item'); } closeChildren(menuItem) { if (menuItem.classList.contains('is-active-item')) { menuItem.classList.remove('is-active-item'); } if (menuItem.classList.contains('has-children')) { const menuButton = menuItem.querySelector('button'); menuButton.setAttribute('aria-expanded', 'false'); const childList = menuItem.querySelector('.hy-menu-level-container'); childList.classList.remove('is-open'); const childItems = childList.querySelectorAll('.hy-menu-item-sidebar'); if (childList.classList.contains('is-active')) { childList.classList.remove('is-active'); } childItems.forEach((element) => { element.classList.remove('is-active-item'); if (element.classList.contains('has-children')) { const menuButton = element.querySelector('button'); menuButton.setAttribute('aria-expanded', 'false'); this.closeChildren(element); } else { return; } }); } } render() { const logoColor = ColorVariant.black; const panelClassAttributes = ['hy-menu-sidepanel--container', this.panelOpen ? 'sidepanel-open' : ''].join(' '); switch (this.menuType) { case MenuType.sidepanel: return ( <div class="hy-menu-sidebar__container" style={{ '--minHeight': `${this.minHeight}px` as 'minHeight', }} > <button aria-label={this.panelToggleAriaLabel} class="hy-menu-sidebar__panel-toggle" onClick={(e) => this.panelToggle(e)} > <hy-icon icon={'hy-icon-caret-left'} fill={'currentColor'} size={10} /> <hy-icon icon={'hy-icon-caret-left'} fill={'currentColor'} size={10} /> {this.panelToggleLabel} </button> <div class={panelClassAttributes} aria-hidden={this.panelOpen ? 'false' : 'true'} aria-expanded={this.panelOpen ? 'true' : 'false'} > <div class="hy-menu-sidepanel__nav-container"> <div class="hy-menu-sidepanel__logo-container"> <div class="hy-menu-sidepanel__logo" aria-hidden={this.panelOpen ? 'false' : 'true'} tabindex={this.panelOpen ? '0' : '-1'} > <hy-site-logo size={56} color={logoColor} url={this.logoUrl} label={this.logoLabel} /> </div> <button aria-label={this.panelToggleCloseAriaLabel} aria-hidden={this.panelOpen ? 'false' : 'true'} tabindex={this.panelOpen ? '0' : '-1'} class="hy-menu-sidebar__panel-toggle" onClick={(e) => this.panelToggle(e)} > <hy-icon icon={'hy-icon-remove'} fill={'currentColor'} size={10} /> {this.panelToggleCloseLabel} </button> </div> <nav role={'navigation'} aria-hidden={this.panelOpen ? 'false' : 'true'} tabindex={this.panelOpen ? '0' : '-1'} class={{ 'hy-menu': true, 'hy-menu-sidepanel': true, 'is-demo': this.isDemo, }} > <slot /> </nav> </div> </div> </div> ); case MenuType.sidenav: return ( <div class="hy-menu-sidebar--container" style={{ '--minHeight': `${this.minHeight}px` as 'minHeight', }} > <div class="hy-menu-sidebar__logo"> <hy-site-logo size={56} color={logoColor} url={this.logoUrl} label={this.logoLabel} /> </div> <button aria-label={this.panelToggleAriaLabel} class="hy-menu-sidebar__panel-toggle" onClick={(e) => this.panelToggle(e)} > <hy-icon icon={'hy-icon-caret-left'} fill={'currentColor'} size={10} /> <hy-icon icon={'hy-icon-caret-left'} fill={'currentColor'} size={10} /> {this.panelToggleLabel} </button> <nav role={'navigation'} aria-hidden={this.menuIsOpen ? 'false' : 'true'} class={{ 'hy-menu': true, 'hy-menu-sidebar': true, 'is-demo': this.isDemo, }} > <slot /> </nav> </div> ); } } }