Skip to content
Snippets Groups Projects
menu-sidebar.tsx 12.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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>
            );
        }
      }
    }