Skip to content
Snippets Groups Projects
menu-level-container.tsx 9.45 KiB
Newer Older
  • Learn to ignore specific revisions
  • import {Component, Element, h, Host, Listen, Prop, State} from '@stencil/core';
    
    import {MenuType} from '../../../utils/utils';
    
    
    @Component({
      tag: 'hy-menu-level-container',
      styleUrl: 'menu-level-container.scss',
    
      shadow: false,
    
    })
    export class MenuLevelContainer {
      @Element() el: HTMLElement;
    
      @Prop() labelFrontPage?: string;
    
      @Prop() menuLevel: number;
      @Prop() menuType: MenuType = MenuType.mobile;
    
      @Prop({reflect: true}) activeTrailTriggered: boolean = false;
    
      @Prop({mutable: true, reflect: true}) depth: number = 0;
    
      @Prop({mutable: true, reflect: true}) triggerItem: string;
    
      /**
       * Url to front page for panel first parent
       */
      @Prop() frontUrl: string;
      /**
       * label for front page for panel first parent
       */
      @Prop() frontLabel: string;
    
      @State() menuIsOpen: boolean = false;
    
    Markus Kalijärvi's avatar
    Markus Kalijärvi committed
      // Add/Remove is-hidden class from each upper level menu-items
      // to make browsing more accessible.
      assignMenuItemClass(parentMenu: Element, type: string) {
        const items = Array.from(parentMenu.children);
        items.forEach((item) => {
          if (type === 'remove') {
            item.classList.remove('is-hidden');
          } else {
            item.classList.add('is-hidden');
          }
        });
      }
    
      // Listener for assigning active-trail for parent menu level container.
    
      @Listen('menuContainerActiveTrail', {target: 'document', capture: true})
      menuContainerActiveTrail(data) {
        const currentMenuContainer = this.el.getAttribute('trigger-item');
        if (currentMenuContainer === data.detail.triggerItem) {
          this.activeTrailTriggered = true;
    
    Markus Kalijärvi's avatar
    Markus Kalijärvi committed
          this.assignMenuItemClass(this.el.parentElement.closest('hy-menu-level-container'), 'add');
    
      // Listener for opening and closing menu level container.
    
      @Listen('menuContainerToggled', {target: 'document', capture: true})
      menuContainerToggled(data) {
        // Toggle submenu.
        if (this.triggerItem == data.detail.triggerItem) {
          this.activeTrailTriggered = false;
    
          this.menuIsOpen = data.detail.triggerType != 'remove';
    
    Markus Kalijärvi's avatar
    Markus Kalijärvi committed
          this.assignMenuItemClass(this.el.parentElement.closest('hy-menu-level-container'), data.detail.triggerType);
    
    
          // Scroll to .hy-menu top.
          let hyMenu = this.el.parentElement.closest('hy-menu');
          hyMenu.shadowRoot.querySelector('.hy-menu').scrollTop = 0;
    
      componentWillUpdate() {
    
        // Pass the menu type and menu-button-submenu-expand attributes to
        // the child menu item component.
    
        if (this.menuType) {
          const items = Array.from(this.el.children);
          items.forEach((item) => {
            item.setAttribute('menu-type', this.menuType);
    
            item.setAttribute('menu-button-submenu-expand', this.menuButtonSubmenuExpand);
    
        // Assign depth value to current menu level container instance;
        // 1st level, 2nd level, etc.
    
        let parentMenu = this.el.closest('hy-menu-level-container');
        let nextParentMenu;
        this.depth = 0;
        while (parentMenu) {
          nextParentMenu = parentMenu.parentElement.closest('hy-menu-level-container');
          if (nextParentMenu === parentMenu) {
            break;
          } else {
            parentMenu = nextParentMenu;
            this.depth = this.depth + 1;
          }
        }
    
    
        // Set trigger item for each mobile menu level container and handle only submenus.
    
        if (this.menuType === MenuType.mobile) {
    
          if (this.menuLevel > 1) {
            const parentMenuItem = this.el.closest('hy-menu-item');
            this.triggerItem = parentMenuItem.getAttribute('menu-link-id');
            this.headingItem = {
              ...this.headingItem,
              url: parentMenuItem.getAttribute('url'),
            };
            this.headingItem = {
              ...this.headingItem,
              label: parentMenuItem.getAttribute('label'),
            };
    
            this.headingItem = {
              ...this.headingItem,
              isActive: window.location.pathname === this.headingItem.url,
            };
    
          } else {
            this.triggerItem = 'home';
          }
        }
    
        // Set is-active-child and menu-level attributes to all sibling menu-items.
    
        if (this.menuType === MenuType.sidenav) {
    
          const parentMenuItem = this.el.closest('hy-menu-item-sidebar');
    
    
          if (parentMenuItem && parentMenuItem.classList.contains('is-active')) {
    
            const items = Array.from(this.el.children);
            items.forEach((item) => {
              item.setAttribute('is-active-child', 'true');
              item.setAttribute('menu-level', this.menuLevel.toString());
            });
          }
    
        }
      }
    
      render() {
        let classAttributes = ['hy-menu-level-container', 'hy-menu-level-container--level-' + this.depth];
    
        switch (this.menuType) {
    
            classAttributes = [...classAttributes, 'hy-menu-level-container--desktop'];
    
              <Host class={classAttributes.join(' ')}>
    
            classAttributes = [...classAttributes, 'hy-menu-level-container--mobile'];
            if (this.depth === 1) {
              classAttributes = [...classAttributes, 'is-open'];
    
              return (
    
                <Host aria-expanded={this.menuIsOpen.toString()} class={classAttributes.join(' ')} tabindex={'-1'}>
    
                  <slot />
                </Host>
              );
            } else {
    
              classAttributes = [...classAttributes, this.menuIsOpen ? 'is-open' : null];
    
                <Host aria-expanded={this.menuIsOpen.toString()} class={classAttributes.join(' ')} tabindex={'-1'}>
    
                  <hy-menu-item
                    label={this.headingItem.label}
                    url={this.headingItem.url}
                    isHeading={true}
    
                    isActive={this.headingItem.isActive}
    
                    menu-type={'mobile'}
                  />
                  <slot />
                </Host>
              );
            }
    
            if (this.depth === 1) {
    
              const shouldBeHidden = this.depth != null && this.depth === 1 ? 'is-open' : '';
    
              classAttributes = [...classAttributes, 'hy-menu-level-container--sidenav', shouldBeHidden];
    
                <ul data-menu-level={this.depth} class={classAttributes.join(' ')}>
    
              const isMenuOpen = this.menuIsOpen ? 'is-open_is-open__sub-level' : null;
              const shouldBeHidden = this.depth != null && this.depth === 1 ? 'is-open' : '';
              const parentMenuItem = this.el.closest('hy-menu-item-sidebar') as any;
              const parentClone = parentMenuItem.cloneNode(true) as any;
              const parentLink = parentClone.querySelector('.hy-menu-item-sidebar__label');
    
              classAttributes = [...classAttributes, 'hy-menu-level-container--sidenav', shouldBeHidden, isMenuOpen];
    
                <ul data-menu-level={this.depth} class={classAttributes.join(' ')}>
                  {parentLink && (
                    <li class="hy-menu-item-sidebar is-parent is-parent--sub-level">
                      <hy-icon
                        class={'hy-menu-item-parent__icon'}
                        icon={'hy-icon-dot-arrow-right'}
                        fill={'currentColor'}
                        size={20}
                      />
                      <span
                        aria-level={this.depth}
                        role="heading"
                        class="hy-menu-item-sidebar--label-container"
                        innerHTML={parentLink.outerHTML}
                      ></span>
                    </li>
                  )}
    
    
          case MenuType.sidepanel:
            //const shouldBeHidden = (this.depth != null && this.depth === 1) ? 'is-open' : '';
    
            classAttributes = [...classAttributes, 'hy-menu-level-container--sidepanel'];
            const parentMenuItem = this.el.closest('hy-menu-item-sidebar') as any;
            const parentClone = parentMenuItem ? (parentMenuItem.cloneNode(true) as any) : null;
            const parentLink = parentClone ? parentClone.querySelector('.hy-menu-item-sidebar__label') : null;
            return (
              <ul data-menu-level={this.depth} class={classAttributes.join(' ')}>
                {parentLink ? (
                  <li class="hy-menu-item-sidebar is-parent is-parent--sub-level">
                    <hy-icon
                      class={'hy-menu-item-parent__icon'}
                      icon={'hy-icon-dot-arrow-right'}
                      fill={'currentColor'}
                      size={20}
                    />
                    <span
                      aria-level={this.depth}
                      role="heading"
                      class="hy-menu-item-sidebar--label-container"
                      innerHTML={parentLink.outerHTML}
                    ></span>
                  </li>
                ) : (
                  <li class="hy-menu-item-sidebar is-parent is-parent--sub-level is-parent--frontpage">
                    <span class={'hy-menu-item__parent__icon'}>
                      <hy-icon
                        class={'hy-menu-item__parent__icon__svg'}
                        icon={'hy-icon-caret-left'}
                        fill={'currentColor'}
                        size={10}
                      />
                    </span>
                    <div class="hy-menu-item-sidebar--label-container">
                      <a class="hy-menu-item-sidebar__label" href={this.frontUrl}>
                        {this.frontLabel}
                      </a>
                    </div>
                  </li>
                )}
                <slot />
              </ul>
            );