Skip to content
Snippets Groups Projects
site-header.tsx 21 KiB
Newer Older
  • Learn to ignore specific revisions
  • export interface DesktopLinks {
      label: string;
      url: string;
      menuLinkId: string;
    }
    
    export interface DonateLink {
      url: string;
      label: string;
    }
    
    export interface ComponentLabels {
      open: string;
      close: string;
      expand?: string;
      label?: string;
    }
    
    import {Component, Element, Event, EventEmitter, h, Listen, Prop, State} from '@stencil/core';
    
    import ResizeObserver from 'resize-observer-polyfill';
    
    import {MenuType, ColorVariant, SiteLogoSize} from '../../utils/utils';
    
    
    @Component({
      tag: 'hy-site-header',
      styleUrl: 'site-header.scss',
    
    })
    export class SiteHeader {
      @Element() el: HTMLElement;
    
    druid's avatar
    druid committed
      @Prop() isGroup: boolean = false;
    
      @Prop() dataMenuLanguage: string;
      @Prop() dataMenuDonate: string;
    
      @Prop() dataSiteSearchLabels: string;
    
      @Prop() logoUrl?: string;
      @Prop() logoLabel?: string;
    
    druid's avatar
    druid committed
      @Prop() siteLabel?: string;
      @Prop() siteUrl?: string;
    
      @Prop() menuLabel: string = 'Menu';
    
      @Prop() menuLabelOpen?: string;
      @Prop() menuLabelClose?: string;
    
      @Prop({reflect: true}) menuType: MenuType = MenuType.default;
    
      /*
      First level menu links to be displayed on Desktop screens.
      * */
      @Prop() dataDesktopLinks: DesktopLinks[] | string;
    
    druid's avatar
    druid committed
      @Prop() dataMainMenuLinks: DesktopLinks[] | string;
    
      @Prop() dataSearchTools: DesktopLinks[] | string;
    
    
      @State() isMobile: boolean;
      @State() isMenuOpen: boolean = false;
    
      @State() isDesktopMenuOpen: boolean = false;
    
    
      @Event() headerScrollUp: EventEmitter;
    
    Tuukka Turu's avatar
    Tuukka Turu committed
      @Event() headerScrollDown: EventEmitter;
    
    druid's avatar
    druid committed
      @Event() menuMobileToggled: EventEmitter;
    
      @Event() menuMobileTopToggled: EventEmitter;
    
    druid's avatar
    druid committed
    
    
      private ro: ResizeObserver;
      private donateLink: DonateLink[];
    
      private menuLabels: ComponentLabels[];
      private searchLabels: ComponentLabels[];
    
      private groupPages: ComponentLabels[];
    
    druid's avatar
    druid committed
      @State() lastScrollTop = 0;
      @State() delta = 5;
      @State() navbarHeight = 0;
      @State() didScroll = false;
      @State() intervalId;
    
    
      // Listener for toggling mobile menu panel on or off.
    
      @Listen('mobileMenuToggle') mobileMenuToggle() {
        this.isMenuOpen = !this.isMenuOpen;
    
    druid's avatar
    druid committed
        this.menuMobileToggled.emit();
    
      // Listener for toggling mobile menu top panel on or off.
      @Listen('mobileMenuTopToggle') mobileMenuTopToggle() {
        this.isMenuOpen = !this.isMenuOpen;
        this.menuMobileTopToggled.emit();
      }
    
    
    druid's avatar
    druid committed
      @Listen('scroll', {target: 'window'})
      handleScroll() {
    
    druid's avatar
    druid committed
        if (this.el.getAttribute('menu-type') === 'desktop') {
          this.didScroll = true;
        }
    
      componentDidLoad() {
    
        // Set the browser resize observer to gather information about browser width.
    
        this.ro = new ResizeObserver((entries) => {
          for (const entry of entries) {
            this.applySizeClasses(entry.contentRect.width);
          }
        });
    
        this.ro.observe(document.body);
    
    
        // Pass the dataMenuLanguage prop to menu component.
    
        this.el.children[0].setAttribute('data-menu-language', this.dataMenuLanguage);
    
    Tuukka Turu's avatar
    Tuukka Turu committed
        this.navbarHeight = this.el.getBoundingClientRect().height;
    
      }
    
      componentWillLoad() {
    
        // Check for site header labels and set them to variables accordingly.
        if (this.dataSiteHeaderLabels) {
          const labels = JSON.parse(this.dataSiteHeaderLabels);
          this.menuLabels = labels.menu_labels;
          this.languageLabels = labels.language_labels;
          this.searchLabels = labels.search_labels;
    
          this.groupPages = labels.group_pages;
    
    
          this.el.children[0].setAttribute('menu-button-breadcrumb-home', this.menuLabels['home']);
          this.el.children[0].setAttribute('menu-button-breadcrumb-main', this.menuLabels['main']);
          this.el.children[0].setAttribute('menu-button-breadcrumb-logourl', this.logoUrl);
    
        }
    
        // Check for the donation link information and set it to variables accordingly.
        // Also pass the donation link data to menu-component.
        if (this.dataMenuDonate) {
          this.donateLink = JSON.parse(this.dataMenuDonate);
          this.el.children[0].setAttribute('data-menu-donate', this.dataMenuDonate);
        }
    
      }
    
      componentWillUpdate() {
    
        // Pass the necessary information to menu component. These props will be
        // used in mobile menu.
    
        this.el.children[0].setAttribute('open', this.isMenuOpen.toString());
        this.el.children[0].setAttribute('logo-label', this.logoLabel);
        this.el.children[0].setAttribute('logo-url', this.logoUrl);
    
    druid's avatar
    druid committed
        this.el.children[0].setAttribute('site-label', this.siteLabel);
        this.el.children[0].setAttribute('site-url', this.siteUrl);
    
        this.el.children[0].setAttribute('menu-button-submenu-expand', this.menuLabels['expand']);
    
        this.el.children[0].setAttribute('menu-button-breadcrumb-return', this.menuLabels['return']);
    
        this.el.children[0].setAttribute('menu-button-breadcrumb-home', this.menuLabels['home']);
        this.el.children[0].setAttribute('menu-button-breadcrumb-main', this.menuLabels['main']);
        this.el.children[0].setAttribute('menu-button-breadcrumb-logourl', this.logoUrl);
    
        this.el.children[0].setAttribute('menu-language-label-open', this.languageLabels['open']);
        this.el.children[0].setAttribute('menu-language-label-close', this.languageLabels['close']);
    
        this.el.children[0].setAttribute('label-front-page', this.menuLabels['front_page']);
    
    druid's avatar
    druid committed
    
        this.intervalId = setInterval(() => {
          this.timer();
        }, 250);
      }
    
      timer() {
        if (this.didScroll) {
          this.hasScrolled();
          this.didScroll = false;
        }
      }
    
      hasScrolled() {
    
        let topOffset = window.pageYOffset;
        const bodyElementClasses = document.querySelector('body').classList;
    
    
          bodyElementClasses.contains('hy-menu-sidepanel__no-scroll') ||
          bodyElementClasses.contains('hy-menu-sidebar__no-scroll')
    
    
        if (Math.abs(this.lastScrollTop - topOffset) <= this.delta) {
    
    druid's avatar
    druid committed
          return;
        }
    
        let hySiteHeader = this.el.shadowRoot.querySelector('.hy-site-header') as HTMLElement;
        // If current position > last position AND scrolled past navbar...
    
        if (topOffset > this.lastScrollTop && topOffset > this.navbarHeight) {
    
    druid's avatar
    druid committed
          // Scroll Down
          hySiteHeader.classList.remove('hy-site-header--sticky-visible');
    
          hySiteHeader.classList.remove('has-toolbar', 'has-toolbar--large');
    
    Tuukka Turu's avatar
    Tuukka Turu committed
    
          // Close Search panel
          //CludoSayt.hide();
          this.headerScrollDown.emit();
    
    druid's avatar
    druid committed
        } else {
          // Scroll Up
    
          if (topOffset < this.el.offsetTop + this.navbarHeight) {
    
    druid's avatar
    druid committed
            hySiteHeader.classList.remove('hy-site-header--sticky-active');
            hySiteHeader.classList.remove('hy-site-header--sticky-visible');
            hySiteHeader.classList.remove('hy-site-header--sticky-hidden');
    
            hySiteHeader.classList.remove('has-toolbar', 'has-toolbar--large');
    
    
            // Close desktop menu panel if it's open.
    
    druid's avatar
    druid committed
          } else {
            hySiteHeader.classList.add('hy-site-header--sticky-active');
            hySiteHeader.classList.remove('hy-site-header--sticky-hidden');
    
            hySiteHeader.classList.add('hy-site-header--sticky-visible');
    
    
            if (bodyElementClasses.contains('toolbar-horizontal')) {
              hySiteHeader.classList.add('has-toolbar');
    
              if (bodyElementClasses.contains('toolbar-tray-open')) {
                hySiteHeader.classList.add('has-toolbar--large');
              }
            }
    
    
        this.lastScrollTop = topOffset;
    
      }
    
      componentDidUnload() {
        this.ro.disconnect();
      }
    
      applySizeClasses(size: number) {
    
        // Set the menu-type based on the width of the browser.
    
          this.menuType = MenuType.tablet;
        } else if (size <= 960) {
    
          this.menuType = MenuType.mobile;
    
          this.menuType = MenuType.desktop;
    
    
        // Pass the menu type to menu component.
    
        this.isMobile = this.menuType === (MenuType.mobile || MenuType.tablet);
        const menuAttribute = this.menuType === MenuType.tablet ? MenuType.mobile : this.menuType;
        this.el.children[0].setAttribute('menu-type', menuAttribute);
    
        const logoSize = this.isMobile ? SiteLogoSize.small : SiteLogoSize.big;
    
    druid's avatar
    druid committed
        const logoSizeGroup = this.isMobile ? SiteLogoSize.small : SiteLogoSize.small;
    
        const logoColor = ColorVariant.black;
    
    druid's avatar
    druid committed
        const logoColorGroup = ColorVariant.white;
    
        let classAttributes = ['hy-site-header', 'hy-site-header--' + this.menuType];
    
        switch (this.menuType) {
          case MenuType.desktop:
    
            // Larger than 1200px screens
    
              <header class={classAttributes.join(' ')}>
    
                <div class={{'hy-backdrop': true, 'is-active': this.isMenuOpen}} />
    
    druid's avatar
    druid committed
                {this.isGroup && (
    
    druid's avatar
    druid committed
                  <div class="hy-site-header__content-top">
    
                    <div class={'hy-site-header__logo-container group '} role="navigation">
    
    druid's avatar
    druid committed
                      <hy-site-logo
                        is-group={true}
                        size={logoSizeGroup}
                        color={logoColorGroup}
                        url={this.siteUrl}
                        label={this.siteLabel}
                      />
    
                      <hy-menu-main-group
                        tabindex="0"
                        is-mobile={false}
                        menu-label={this.groupPages['university_main_menu'] ?? null}
                        data-main-menu={this.dataMainMenuLinks}
                      />
    
    druid's avatar
    druid committed
                    </div>
    
                    <div class={'menu--secondary menu--secondary--group'}>
    
    druid's avatar
    druid committed
                      <hy-menu-language
    
                        class={'menu--secondary__item is-first group'}
    
    druid's avatar
    druid committed
                        is-group={true}
    
    druid's avatar
    druid committed
                        is-mobile={false}
                        data-menu-language={this.dataMenuLanguage}
                        labels={this.languageLabels}
                      />
                      <hy-site-search
    
                        class={'menu--secondary__item group'}
    
    druid's avatar
    druid committed
                        color={logoColorGroup}
                        is-group={true}
    
    druid's avatar
    druid committed
                        show-label={true}
                        labels={this.searchLabels}
                        search-labels={this.dataSiteSearchLabels}
                        search-tools={this.dataSearchTools}
                      />
                      {this.donateLink.map((i) => {
                        return (
    
                          <a class={'menu--secondary__item hy-link__donate group'} href={i.url}>
    
                            <hy-icon icon={'hy-icon-heart-support'} size={16} fill={logoColorGroup} />
    
                            <span class={'hy-link__donate__label group'}>{i.label}</span>
    
    druid's avatar
    druid committed
                          </a>
                        );
                      })}
                    </div>
                  </div>
                )}
    
    druid's avatar
    druid committed
                <div class={{'hy-site-header__content': true, group: this.isGroup}}>
    
                  <div class={'hy-site-header__logo-container'}>
    
    druid's avatar
    druid committed
                    {this.isGroup ? (
    
    druid's avatar
    druid committed
                      <a class={'group'} href={this.logoUrl}>
                        {this.logoLabel}
                      </a>
    
    druid's avatar
    druid committed
                    ) : (
                      <hy-site-logo size={logoSize} color={logoColor} url={this.logoUrl} label={this.logoLabel} />
                    )}
    
                  <hy-desktop-menu-links data-desktop-links={this.dataDesktopLinks}></hy-desktop-menu-links>
    
    druid's avatar
    druid committed
                  {!this.isGroup && (
                    <div class={'menu--secondary'}>
                      <hy-menu-language
                        class={'menu--secondary__item is-first'}
                        is-mobile={false}
                        data-menu-language={this.dataMenuLanguage}
                        labels={this.languageLabels}
                      />
                      <hy-site-search
                        class={'menu--secondary__item'}
                        size={14}
                        color={ColorVariant.black}
                        show-label={true}
                        labels={this.searchLabels}
                        search-labels={this.dataSiteSearchLabels}
                        search-tools={this.dataSearchTools}
                      />
                      {this.donateLink.map((i) => {
                        return (
                          <a class={'menu--secondary__item hy-link__donate'} href={i.url}>
                            <hy-icon icon={'hy-icon-heart-support'} size={14} fill={ColorVariant.black} />
                            <span class={'hy-link__donate__label'}>{i.label}</span>
                          </a>
                        );
                      })}
                    </div>
                  )}
    
          case MenuType.tablet:
    
            // 960px-1200px screens
    
            return (
              <header class={classAttributes.join(' ')}>
                <div class={{'hy-backdrop': true, 'is-active': this.isMenuOpen}} />
    
                {this.isGroup && (
    
    druid's avatar
    druid committed
                  <div class="hy-site-header__content-top">
    
                    <div class={'hy-site-header__logo-container group '} role="navigation">
    
    katja's avatar
    katja committed
                      <hy-site-logo
                        is-group={true}
                        size={logoSizeGroup}
                        color={logoColorGroup}
                        url={this.siteUrl}
                        label={this.siteLabel}
                      />
    
                      <hy-menu-main-group
                        is-mobile={false}
                        menu-label={this.groupPages['university_main_menu'] ?? ''}
                        data-main-menu={this.dataMainMenuLinks}
                      />
    
                    </div>
    
                    <div class={'menu--secondary menu--secondary--group'}>
                      <hy-menu-language
                        class={'menu--secondary__item is-first group'}
    
    druid's avatar
    druid committed
                        is-group={true}
    
                        is-mobile={false}
                        data-menu-language={this.dataMenuLanguage}
                        labels={this.languageLabels}
                      />
                      <hy-site-search
                        class={'menu--secondary__item group'}
    
    druid's avatar
    druid committed
                        color={logoColorGroup}
    
    druid's avatar
    druid committed
                        is-group={true}
    
                        labels={this.searchLabels}
                        search-labels={this.dataSiteSearchLabels}
                        search-tools={this.dataSearchTools}
                      />
                      {this.donateLink.map((i) => {
                        return (
                          <a class={'menu--secondary__item hy-link__donate group'} href={i.url}>
    
                            <hy-icon icon={'hy-icon-heart-support'} size={16} fill={logoColorGroup} />
    
                            <span class={'hy-link__donate__label group'}>{i.label}</span>
                          </a>
                        );
                      })}
                    </div>
    
                )}
                <div class="hy-site-header__content">
                  <div class={'hy-site-header__logo-container'}>
                    {this.isGroup ? (
    
    druid's avatar
    druid committed
                      <a class={'group'} href={this.logoUrl}>
                        {this.logoLabel}
                      </a>
    
                    ) : (
                      <hy-site-logo size={logoSize} color={logoColor} url={this.logoUrl} label={this.logoLabel} />
                    )}
                  </div>
                  {!this.isGroup && (
                    <div class={'menu--secondary'}>
                      <hy-menu-language
                        class={'menu--secondary__item is-first'}
                        is-mobile={false}
                        data-menu-language={this.dataMenuLanguage}
                        labels={this.languageLabels}
                      />
                      <hy-site-search
                        class={'menu--secondary__item'}
                        size={14}
                        color={ColorVariant.black}
                        show-label={true}
                        labels={this.searchLabels}
                        search-labels={this.dataSiteSearchLabels}
                        search-tools={this.dataSearchTools}
                      />
                      {this.donateLink.map((i) => {
                        return (
                          <a class={'menu--secondary__item hy-link__donate'} href={i.url}>
                            <hy-icon icon={'hy-icon-heart-support'} size={14} fill={ColorVariant.black} />
                            <span class={'hy-link__donate__label'}>{i.label}</span>
                          </a>
                        );
                      })}
                    </div>
                  )}
    
                  <div class={'hy-site-header__menu-container'}>
                    <span
                      class={{
                        'hy-site-header__menu-label': true,
                        'is-visible': this.isMenuOpen,
                      }}
                    >
                      {this.menuLabel}
                    </span>
                    <button
                      onClick={() => this.mobileMenuToggle()}
                      class={{
                        'hy-site-header__panel-toggle': true,
                        'is-open': this.isMenuOpen,
                      }}
                      aria-label={this.isMenuOpen ? this.menuLabels['close'] : this.menuLabels['open']}
                    >
                      {this.isMenuOpen ? (
                        <span class="hy-site-header__panel-toggle__label">
                          {this.menuLabelClose}
                          <hy-icon icon={'hy-icon-remove'} size={20} fill={ColorVariant.black} />
                        </span>
                      ) : (
                        <span class="hy-site-header__panel-toggle__label">
                          {this.menuLabelOpen}
                          <hy-icon icon={'hy-icon-hamburger'} size={20} fill={ColorVariant.black} />
                        </span>
                      )}
                    </button>
                    <div
                      class={{
                        'is-open': this.isMenuOpen,
                        'hy-site-header__panel': true,
                      }}
                    >
                      <slot name={'menu'} />
                    </div>
    
                  </div>
                </div>
              </header>
            );
          case MenuType.mobile:
    
            // Smaller than 960px screens
    
            return (
              <header class={classAttributes.join(' ')}>
                <div class={{'hy-backdrop': true, 'is-active': this.isMenuOpen}} />
    
                {this.isGroup && (
    
    Tuukka Turu's avatar
    Tuukka Turu committed
                  <div class="hy-site-header__content-top" is-mobile={true}>
    
                    <div class={'hy-site-header__logo-container group '}>
    
    druid's avatar
    druid committed
                      <hy-site-logo size={logoSizeGroup} color={logoColorGroup} url={this.siteUrl} label={this.siteLabel} />
    
                    </div>
    
                    <div class={'menu--secondary menu--secondary--group'}>
                      <hy-site-search
                        class={'menu--secondary__item group'}
    
    Tuukka Turu's avatar
    Tuukka Turu committed
                        size={18}
    
    druid's avatar
    druid committed
                        color={logoColorGroup}
                        is-group={true}
    
                        show-label={true}
                        labels={this.searchLabels}
                        search-labels={this.dataSiteSearchLabels}
                        search-tools={this.dataSearchTools}
                      />
    
    Tuukka Turu's avatar
    Tuukka Turu committed
                      <hy-menu-main-group
                        site-label={this.siteLabel}
                        site-url={this.siteUrl}
                        logo-label={this.logoLabel}
                        logo-url={this.logoUrl}
                        is-mobile={true}
                        donate={this.donateLink}
                        data-main-menu={this.dataMainMenuLinks}
                      />
    
                <div class="hy-site-header__content">
                  <div class={'hy-site-header__logo-container'}>
    
    druid's avatar
    druid committed
                      <a class={'group'} href={this.logoUrl}>
                        {this.logoLabel}
                      </a>
    
                    ) : (
                      <hy-site-logo size={logoSize} color={logoColor} url={this.logoUrl} label={this.logoLabel} />
                    )}
    
    Tuukka Turu's avatar
    Tuukka Turu committed
                  <div class={{'hy-site-header__menu-container': true, 'is-open': this.isMenuOpen}}>
    
                    {!this.isGroup && (
                      <hy-site-search
                        class={'menu--secondary__item'}
                        size={14}
                        color={ColorVariant.black}
                        show-label={true}
                        labels={this.searchLabels}
                        search-labels={this.dataSiteSearchLabels}
                        search-tools={this.dataSearchTools}
                      />
                    )}
    
                    <button
                      onClick={() => this.mobileMenuToggle()}
                      class={{
                        'hy-site-header__panel-toggle': true,
                        'is-open': this.isMenuOpen,
                      }}
                      aria-label={this.isMenuOpen ? this.menuLabels['close'] : this.menuLabels['open']}
                    >
                      {this.isMenuOpen ? (
                        <span class="hy-site-header__panel-toggle__label">
                          {this.menuLabelClose}
                          <hy-icon icon={'hy-icon-remove'} size={20} fill={ColorVariant.black} />
                        </span>
                      ) : (
                        <span class="hy-site-header__panel-toggle__label">
                          {this.menuLabelOpen}
                          <hy-icon icon={'hy-icon-hamburger'} size={20} fill={ColorVariant.black} />
                        </span>
                      )}
                    </button>
                    <div
                      class={{
                        'is-open': this.isMenuOpen,
                        'hy-site-header__panel': true,
                      }}
                    >
                      <slot name={'menu'} />
                    </div>
    
                  </div>
                </div>
              </header>
            );
        }