Skip to content
Snippets Groups Projects
hy-breadcrumbs.tsx 9.91 KiB
Newer Older
  • Learn to ignore specific revisions
  • Tuukka Turu's avatar
    Tuukka Turu committed
    export interface Breadcrumb {
      url: string;
      text: string;
    }
    let breadcrumbsWidth = null;
    import {Component, Element, h, Listen, Prop, State, Watch} from '@stencil/core';
    import {BreadcrumbVariants} from '../../utils/utils';
    
    @Component({
      tag: 'hy-breadcrumbs',
      styleUrl: 'hy-breadcrumbs.scss',
      shadow: false,
    })
    export class HyBreadcrumbs {
      private _dataItems: Breadcrumb[];
      @Prop() dataItems: Breadcrumb[] | string;
      @Prop() variant: BreadcrumbVariants = BreadcrumbVariants.default as any;
      @Prop() headerstyle: string = 'with-sidebar';
    
      @State() menuOpen: boolean = false;
      @Element() el: HTMLElement;
    
      @Watch('dataItems')
      arrayDataWatcher(newValue: Breadcrumb[] | string) {
        if (typeof newValue === 'string') {
          this._dataItems = JSON.parse(newValue);
        } else {
          this._dataItems = newValue;
        }
      }
      componentWillLoad() {
        this.arrayDataWatcher(this.dataItems);
      }
    
      componentDidLoad() {
        let hyMainDiv = this.el.closest('.hy-main');
        if (hyMainDiv) {
          if (!hyMainDiv.classList.contains('with-sidebar')) {
            this.headerstyle = 'without-sidebar';
          }
        }
    
        // Set breadcumbs width + paddings.
        breadcrumbsWidth = this.el.offsetWidth + 64;
    
        const layoutContentElement = document.getElementsByClassName('layout-content')[0] as HTMLElement;
        const moreButton = document.querySelectorAll('.breadcrumb-item-dropdown-button')[0];
    
        if (layoutContentElement) {
          if (breadcrumbsWidth >= layoutContentElement.offsetWidth) {
            moreButton.setAttribute('aria-hidden', 'false');
            this.adjustBreadcrumbsMenuVisibility();
          }
    
    Tuukka Turu's avatar
    Tuukka Turu committed
        }
      }
    
      adjustBreadcrumbsMenuVisibility(showMenu = true) {
        // Show ... and Hide intermediate links
        if (!showMenu) {
          this.closeMoreMenu();
        }
    
        const crumbContainer = document.querySelectorAll('.hy-breadcrumbs')[0];
        const moreDotsItem = document.querySelectorAll('#more')[0];
        const moreDotsItemWrapper = document.querySelectorAll('.breadcrumb-item__more')[0];
        if (moreDotsItem) {
          if (showMenu) {
            crumbContainer.classList.add('is-condensed');
            moreDotsItem.classList.add('visible');
            moreDotsItemWrapper.classList.add('visible');
          } else {
            crumbContainer.classList.remove('is-condensed');
            moreDotsItem.classList.remove('visible');
            moreDotsItemWrapper.classList.remove('visible');
          }
        }
    
        const intermediateItems = document.querySelectorAll('.intermediate');
        if (intermediateItems) {
          for (let i = 0; i < intermediateItems.length; i++) {
            if (showMenu) {
              intermediateItems[i].classList.add('hidden');
            } else {
              intermediateItems[i].classList.remove('hidden');
            }
          }
        }
      }
    
      HomeItem(url) {
        const homeItemClass = ['hy-icon-wrapper', this.variant].join(' ');
        return (
          <li class="breadcrumb-item home">
            <a href={url} class={homeItemClass}>
    
              <hy-icon icon={'hy-icon-home'} class={`${this.variant}`} size={16} />
    
    Tuukka Turu's avatar
    Tuukka Turu committed
            </a>
    
            <span class="breadcrumb-item__divider">/</span>
    
    Tuukka Turu's avatar
    Tuukka Turu committed
          </li>
        );
      }
    
      BreadcrumbItem(label, url, className = '', withCaret = true) {
        const breadcrumbClass = ['breadcrumb-item', className].join(' ');
        if (url) {
          if (withCaret) {
            return (
              <li class={breadcrumbClass}>
                <a href={url} class={`${this.variant}`}>
                  {label}
                </a>
    
                <span class="breadcrumb-item__divider">/</span>
    
    Tuukka Turu's avatar
    Tuukka Turu committed
              </li>
            );
          } else {
            return (
              <li class={breadcrumbClass}>
                <a href={url} class={`${this.variant}`}>
                  {label}
                </a>
              </li>
            );
          }
        } else {
          return (
            <li class={`${breadcrumbClass} breadcrumb-item__current`}>
              <a aria-current="page" href={url} class={`${this.variant}`}>
    
                {label.length > 20 ? `${label.substring(0, 19)}...` : label}
    
    Tuukka Turu's avatar
    Tuukka Turu committed
              </a>
            </li>
          );
        }
      }
    
      BreadcrumbTextItem(label, className = '') {
        const breadcrumbClass = ['breadcrumb-item', className].join(' ');
        return <li class={breadcrumbClass}>{label}</li>;
      }
    
    
      DropdownMenuItem(items) {
    
    Tuukka Turu's avatar
    Tuukka Turu committed
        return (
          <li class="breadcrumb-item__more">
            <button
              type="button"
              aria-hidden="true"
              aria-expanded="false"
              id="more"
              key="more"
              class="breadcrumb-item-dropdown-button"
    
              aria-label="Open breadcrumb navigation"
    
    Tuukka Turu's avatar
    Tuukka Turu committed
            >
    
              <span class="breadcrumb-item-dropdown-button__content">
                {/* Span is for ... */}
                <span></span>
                <hy-icon
                  icon={'hy-icon-caret-right'}
                  class={'breadcrumb-item-caret__drop breadcrumb-item__more__icon'}
                  size={10}
                />
              </span>
    
    Tuukka Turu's avatar
    Tuukka Turu committed
            </button>
    
            <ol class="breadcrumb-hidden-items" aria-hidden="true">
              {items}
            </ol>
            <span class="breadcrumb-item__divider">/</span>
    
    Tuukka Turu's avatar
    Tuukka Turu committed
          </li>
        );
      }
    
      adjustHiddenMenuWidth() {
        // set width to the menu area equal to the widest link + paddings
        const moreMenu = document.querySelectorAll('.breadcrumb-hidden-items')[0];
        if (moreMenu) {
          if (document.body.scrollWidth < 480) {
            (moreMenu as HTMLElement).style.width = '100%';
          } else {
            //maxIntermediateLinkWidth
            var maxIntermediateLinkWidth = 0;
            const moreMenuLinks = document.querySelectorAll('.breadcrumb-hidden-items .breadcrumb-item a');
            if (moreMenuLinks) {
              for (let i = 0; i < moreMenuLinks.length; i++) {
                if (maxIntermediateLinkWidth < (moreMenuLinks[i] as HTMLElement).offsetWidth) {
                  maxIntermediateLinkWidth = (moreMenuLinks[i] as HTMLElement).offsetWidth;
                }
              }
              maxIntermediateLinkWidth = maxIntermediateLinkWidth + 32 + 64;
            }
            (moreMenu as HTMLElement).style.width = maxIntermediateLinkWidth.toString().concat('px');
          }
        }
      }
    
      closeMoreMenu() {
        const moreMenu = document.querySelectorAll('.breadcrumb-hidden-items')[0];
        if (moreMenu) {
          moreMenu.classList.remove('breadcrumb-hidden-items__is-open');
          this.menuOpen = false;
        }
        const moreBreadcrumb = document.querySelectorAll('#more')[0];
        if (moreBreadcrumb) {
          moreBreadcrumb.classList.remove('is-open');
        }
      }
    
      // When a ... is clicked, show/hide the Menu with hidden breadcrumbs
      @Listen('click')
      clickEventListener(event) {
        if (!event) return;
    
        const target = event.target;
        const moreMenu = document.querySelectorAll('.breadcrumb-hidden-items')[0];
        const moreButton = document.querySelectorAll('.breadcrumb-item-dropdown-button')[0];
    
        // Trigger if target is button or svg icon
        // TODO: Make this if prettier
        if (
          target &&
          (target.id === 'more' ||
            ((target.tagName == 'svg' || 'path') &&
              target.closest('hy-icon').classList.contains('breadcrumb-item__more__icon')))
        ) {
          //@todo Show the menu on the right place of the screen
          if (moreMenu) {
            if (this.menuOpen) {
              moreMenu.classList.remove('breadcrumb-hidden-items__is-open');
    
              moreMenu.setAttribute('aria-hidden', 'true');
    
    Tuukka Turu's avatar
    Tuukka Turu committed
              moreButton.classList.remove('is-open');
              moreButton.setAttribute('aria-expanded', 'false');
            } else {
              moreMenu.classList.add('breadcrumb-hidden-items__is-open');
              moreButton.classList.add('is-open');
    
              moreMenu.setAttribute('aria-hidden', 'false');
    
    Tuukka Turu's avatar
    Tuukka Turu committed
              moreButton.setAttribute('aria-expanded', 'true');
            }
            this.menuOpen = !this.menuOpen;
          }
        } else {
          this.closeMoreMenu();
        }
    
        event.stopPropagation();
        event.stopImmediatePropagation();
      }
    
      @Listen('resize', {target: 'window'})
      resizeEventListener(event) {
        if (!event) return;
    
        const breadcrumbsElement = document.querySelectorAll('.hy-breadcrumbs')[0];
    
        const moreButton = document.querySelectorAll('.breadcrumb-item-dropdown-button')[0];
    
    
    Tuukka Turu's avatar
    Tuukka Turu committed
        if (breadcrumbsElement) {
          if (breadcrumbsWidth + 64 >= document.body.scrollWidth) {
    
            moreButton.setAttribute('aria-hidden', 'false');
            this.adjustBreadcrumbsMenuVisibility();
    
    Tuukka Turu's avatar
    Tuukka Turu committed
          } else {
    
            moreButton.setAttribute('aria-hidden', 'true');
    
    Tuukka Turu's avatar
    Tuukka Turu committed
            this.adjustBreadcrumbsMenuVisibility(false);
          }
        }
      }
    
      render() {
        //@todo Accesibility
        const TOTAL_ITEMS = this._dataItems.length;
        const MAX_ITEMS_TO_SHOW = 3;
    
        let isMenuNeeded = TOTAL_ITEMS > MAX_ITEMS_TO_SHOW;
    
        let itemsBreadcrumbs = [];
        let itemsToShowInMenu = [];
    
        if (this.variant == BreadcrumbVariants.landingLarge) {
          // Landing pages, Large variant
          this._dataItems.map((x, index) => {
            if (index < 2) {
              if (index == 0) {
                itemsBreadcrumbs.push(this.HomeItem(x.url));
              } else {
                itemsBreadcrumbs.push(this.BreadcrumbItem(x.text, '', 'main'));
              }
            }
          });
        } else {
          // Landing and Content pages, Standard variant
          this._dataItems.map((x, index) => {
            let breadcrumbEl = this.BreadcrumbItem(x.text, x.url, '', false);
    
    
            if (isMenuNeeded && index > 0 && index < TOTAL_ITEMS - 1) {
              itemsToShowInMenu.push(<li>{breadcrumbEl}</li>);
    
    Tuukka Turu's avatar
    Tuukka Turu committed
              itemsBreadcrumbs.push(this.BreadcrumbItem(x.text, x.url, 'intermediate'));
              return;
            } else {
              if (index == 0) {
                itemsBreadcrumbs.push(this.HomeItem(x.url));
              } else {
                itemsBreadcrumbs.push(this.BreadcrumbItem(x.text, x.url, 'main'));
              }
            }
          });
        }
    
    
        // Add items to show in breadcrumb popup in correct DOM position.
        itemsBreadcrumbs.splice(1, 0, this.DropdownMenuItem(itemsToShowInMenu));
    
    
    Tuukka Turu's avatar
    Tuukka Turu committed
        const breadcrumbsClass = ['hy-breadcrumbs', this.variant, this.headerstyle].join(' ');
    
        return (
          <nav aria-label="Breadcrumb" role="navigation" aria-labelledby="system-breadcrumb" class={breadcrumbsClass}>
            <ol class="breadcrumb-container">{itemsBreadcrumbs}</ol>
          </nav>
        );
      }
    }