Skip to content
Snippets Groups Projects
menu-item.tsx 9.32 KiB
Newer Older
  • Learn to ignore specific revisions
  • import {Component, Element, Event, EventEmitter, h, Host, Listen, Prop} from '@stencil/core';
    import {MenuType} from '../../../utils/utils';
    
    
    // import {MenuItemVariants} from '../../utils/utils';
    
    
    @Component({
      tag: 'hy-menu-item',
      styleUrl: 'menu-item.scss',
    
      shadow: true,
    
      @Event() addBreadcrumb: EventEmitter;
      @Event() menuContainerActiveTrail: EventEmitter;
      @Event() menuContainerToggled: EventEmitter;
      @Event() routeClicked: EventEmitter;
    
      @Prop() inActiveTrail: boolean = false;
      @Prop() isActive: boolean = false;
    
      @Prop() isActiveChild?: boolean = false;
    
      @Prop() isParent: boolean = false;
    
      @Prop() menuItemAlternative: boolean = false;
      @Prop() menuLinkId: string = '';
    
      @Prop() menuType: MenuType = MenuType.desktop;
    
      @Prop() menuButtonSubmenuExpand?: string = '';
    
      @Prop({mutable: true}) ariaExpanded: boolean = false;
      @Prop({mutable: true, reflect: true}) depth: number = 0;
      @Prop({mutable: true}) hasChildren: boolean = null;
    
      @Prop({mutable: true}) label: string = '';
    
      @Prop({mutable: true}) parentAsHeading: string = '';
    
      @Prop({mutable: true, reflect: true}) parentExpanded: boolean = false;
      @Prop({mutable: true}) url: string = '';
    
      // Listener for submenu expand button. Listener will toggle menu level
      // container and add its parent to breadcrumbs.
    
      @Listen('handleClick', {capture: true}) handleClick() {
    
        this.menuContainerToggled.emit({
          triggerItem: this.menuLinkId,
          triggerType: 'add',
        });
    
        const currentParent = this.el.parentNode;
        this.addBreadcrumb.emit({
          url: this.url,
          label: currentParent.parentElement.getAttribute('label'),
    
          bid: this.menuLinkId,
    
        // If is-active class is added by system, add it to menu component as well.
        if (this.el.classList.contains('is-active')) {
          this.isActive = true;
        }
    
    
        // Notify breadcrumbs if item is in active trail.
        if (this.inActiveTrail && !this.isActive) {
          const currentParent = this.el.parentNode;
          this.addBreadcrumb.emit({
            url: this.url,
            label: currentParent.parentElement.getAttribute('label'),
    
            bid: this.menuLinkId,
    
          if (this.menuType === MenuType.sidenav) {
    
            this.el.children[0].setAttribute('class', 'is-open');
          }
    
        // If current menu item is active, trigger all parent menuLevelContainer
        // elements in the same active-trail to open the menu.
    
        if (this.isActive) {
          const getParents = (elem) => {
            let parents = [];
    
            while (elem.parentNode && elem.parentNode.nodeName.toLowerCase() != 'hy-menu') {
    
              elem = elem.parentNode;
              parents.push(elem);
            }
            return parents;
          };
    
          const parents = getParents(this.el);
          parents.forEach((element) => {
            if (element.tagName.toLowerCase() === 'hy-menu-item') {
    
              this.menuContainerActiveTrail.emit({
                triggerItem: element.getAttribute('menu-link-id'),
              });
    
          // If side navigation menu item has is-active state, prepare the menu items
          // for the last children.
    
          if (this.menuType === MenuType.sidenav) {
    
            parents.forEach((element) => {
              if (element.tagName.toLowerCase() === 'hy-menu-item') {
                element.classList.add('is-hidden--child');
              }
            });
    
    
            // If current active menu item have children, set the child
            // menu-level-container open.
            if (this.el.children.length > 0) {
    
              this.el.children[0].setAttribute('class', 'is-open');
    
            }
            // If current active menu item does not have any children, mock the
            // current menu item and parent menu item to behave like there would
            // be child menu items available.
            else {
    
              const parentMenuItem = this.el.parentElement.closest('hy-menu-item');
              const parentMenuItemAnchor = parentMenuItem.shadowRoot.querySelector('a.hy-menu-item--sidenav');
              parentMenuItem.classList.remove('is-hidden--child');
    
              parentMenuItemAnchor.classList.add('is-active--heading');
              const menuItemSiblings = Array.prototype.slice.call(this.el.closest('hy-menu-level-container').children);
              menuItemSiblings.forEach((element) => {
                if (element.tagName.toLowerCase() === 'hy-menu-item') {
                  element.classList.add('is-active--child');
                }
              });
    
        // Assign depth value to current menu item instance; 1st level, 2nd level, etc.
    
        this.hasChildren = this.el.getElementsByTagName('hy-menu-level-container').length >= 1;
    
        let parentMenuItem = this.el.closest('hy-menu-item');
        let nextParentMenuItem;
    
        while (parentMenuItem) {
          nextParentMenuItem = parentMenuItem.parentElement.closest('hy-menu-item');
          if (nextParentMenuItem === parentMenuItem) {
    
            if (nextParentMenuItem !== null) {
              this.parentAsHeading = nextParentMenuItem;
    
            parentMenuItem = nextParentMenuItem;
    
        switch (this.menuType) {
    
            return (
              <Host
                class={{
                  'is-active': this.isActive,
                  'hy-menu-item': true,
                  'hy-menu-item--alternative': this.menuItemAlternative,
                  'hy-menu-item--desktop': true,
    
                  'is-demo': this.isDemo,
    
                }}
              >
                <a
                  aria-current={this.isHeading.toString()}
                  href={this.url}
                  class={{
                    'is-active': this.isActive,
                    'in-active-trail': this.inActiveTrail,
                    'is-heading': this.isHeading,
                    'hy-menu-item--desktop': true,
    
                    'is-demo': this.isDemo,
    
                  {this.depth == 1 && (
                    <span class="hy-menu-item__heading__icon">
    
    Ekaterina Kondareva's avatar
    Ekaterina Kondareva committed
                      <hy-icon icon={'hy-icon-arrow-right'} size={40} />
    
                    </span>
                  )}
                  {this.depth == 2 && (
                    <span class="hy-menu-item__heading__icon">
    
    Ekaterina Kondareva's avatar
    Ekaterina Kondareva committed
                      <hy-icon icon={'hy-icon-caret-right'} size={12} />
    
                  <span class={'hy-menu-item__label'}>{this.label}</span>
                </a>
    
                {this.hasChildren && <slot />}
    
            return (
              <Host
                class={{
                  'is-active': this.isActive,
                  'hy-menu-item': true,
                  'hy-menu-item--alternative': this.menuItemAlternative,
                  'hy-menu-item--mobile': true,
    
                  'is-demo': this.isDemo,
    
                }}
              >
                <a
                  aria-current={this.isHeading.toString()}
                  href={this.url}
                  class={{
                    'is-active': this.isActive,
                    'in-active-trail': this.inActiveTrail,
                    'is-heading': this.isHeading,
                    'hy-menu-item--mobile': true,
    
                    'is-demo': this.isDemo,
    
                  }}
                >
                  <span class={'hy-menu-item__label'}>{this.label}</span>
                </a>
    
                {this.hasChildren && (
                  <button
                    aria-haspopup={'true'}
                    aria-label={`Open submenu for ${this.label}`}
                    onClick={() => this.handleClick()}
                    class={'hy-menu-item__button'}
                  >
    
                    <hy-icon icon={'hy-icon-caret-right'} size={14} />
    
                {this.hasChildren && <slot />}
              </Host>
            );
    
    
            let classAttributes = [
              'hy-menu-item--sidenav',
              this.isDemo ? 'is-demo' : '',
              this.isActive ? 'is-active' : '',
              this.isParent ? 'is-parent' : '',
              this.isActiveChild ? 'is-active--child' : '',
              this.inActiveTrail ? 'in-active-trail' : '',
              this.depth != null ? 'hy-menu-item--level-' + this.depth : '',
    
              this.isActive && this.hasChildren ? 'is-active--heading' : '',
    
            ];
            let anchorClassAttributes = [...classAttributes, this.isHeading ? 'is-heading' : ''];
            classAttributes = [...classAttributes, 'hy-menu-item'];
    
            return this.isParent ? (
              <Host class={classAttributes.join(' ')}>
                <a href={this.url} class={anchorClassAttributes.join(' ')}>
                  <span class={'hy-menu-item__label__icon'}>
    
                    <hy-icon icon={'hy-icon-arrow-left'} fill={'currentColor'} size={18} />
    
                  <span class={'hy-menu-item__label'}>{this.label}</span>
                </a>
    
              </Host>
            ) : (
              <Host class={classAttributes.join(' ')}>
                <a aria-current={this.isHeading.toString()} href={this.url} class={anchorClassAttributes.join(' ')}>
                  <span class={'hy-menu-item__label'}>{this.label}</span>
                  {this.hasChildren && (
                    <span class={'hy-menu-item__label__icon'}>
                      <hy-icon icon={'hy-icon-caret-right'} fill={'currentColor'} size={12} />
                    </span>
                  )}
                </a>
    
                {this.hasChildren && <slot />}
              </Host>
            );
        }