Skip to content
Snippets Groups Projects
site-search.tsx 9.27 KiB
Newer Older
  • Learn to ignore specific revisions
  • export interface SearchTools {
    
    druid's avatar
    druid committed
      label: string;
    
      url: string;
      description: string;
      menuLinkId: string;
      isExternal: string;
    }
    
    export interface SearchLabels {
      label?: string;
    
    druid's avatar
    druid committed
    }
    
    
    declare global {
      interface Window {
        CludoSayt: any;
      }
    }
    
    
    druid's avatar
    druid committed
    import {Component, Prop, h, Watch, State, Listen, Host, Event, EventEmitter, Element} from '@stencil/core';
    
    import {ComponentLabels} from '../site-header';
    
    import {ColorVariant} from '../../../utils/utils';
    
    
    @Component({
      tag: 'hy-site-search',
      styleUrl: 'site-search.scss',
    
    })
    export class SiteSearch {
    
    druid's avatar
    druid committed
      @Element() el: HTMLElement;
    
    
      @Prop() color: ColorVariant = ColorVariant.black;
    
      @Prop() isAlternative: boolean = false;
    
    druid's avatar
    druid committed
      @Prop() isGroup: boolean = false;
    
    Markus Kalijärvi's avatar
    Markus Kalijärvi committed
      @Prop() labels?: ComponentLabels[] | string;
    
      @Prop() searchLabels: string;
      @Prop() searchTools: string;
      private _searchTools: SearchTools[];
    
    
      @Prop() showLabel: boolean = false;
    
    druid's avatar
    druid committed
      @Prop() dataSearchSpecialTools: string;
    
    
      private _searchLabels: SearchLabels[];
      private searchTitleLabel: string;
      private searchCloseLabel: string;
      private searchToolsLabel: string;
      private searchDescriptionLabel: string;
    
    druid's avatar
    druid committed
    
      @State() isSearchPanelOpen: boolean = false;
      @Event() searchPanelToggled: EventEmitter;
    
    
    Markus Kalijärvi's avatar
    Markus Kalijärvi committed
      private _labels: ComponentLabels[];
    
      @Watch('labels') labelsWatcher(data: ComponentLabels[] | string) {
        this._labels = typeof data === 'string' ? JSON.parse(data) : data;
      }
    
    
    druid's avatar
    druid committed
      componentWillLoad() {
    
        // Special search tools.
        if (this.searchTools) {
          this._searchTools = JSON.parse(this.searchTools);
        }
    
        if (this.searchLabels) {
          this._searchLabels = JSON.parse(this.searchLabels);
    
          this.searchTitleLabel = this._searchLabels['search_label'];
          this.searchCloseLabel = this._searchLabels['search_close_label'];
          this.searchToolsLabel = this._searchLabels['search_tools_label'];
          this.searchDescriptionLabel = this._searchLabels['search_description'];
    
    druid's avatar
    druid committed
        }
      }
    
    
    Markus Kalijärvi's avatar
    Markus Kalijärvi committed
      componentWillRender() {
        this.labelsWatcher(this.labels);
      }
    
    druid's avatar
    druid committed
      // CLose the search panel if user opens the desktop menu panel.
      @Listen('menuDesktopToggled', {target: 'document'})
      desktopMenuToggled() {
    
    Tuukka Turu's avatar
    Tuukka Turu committed
        this.closeSearchPanel();
    
    druid's avatar
    druid committed
      }
    
      // CLose the search panel if user opens the mobile menu panel.
      @Listen('menuMobileToggled', {target: 'document'})
      mobileMenuToggled() {
    
    Tuukka Turu's avatar
    Tuukka Turu committed
        this.closeSearchPanel();
    
    druid's avatar
    druid committed
      }
    
      // CLose the search panel if user opens the language menu.
      @Listen('menuLanguageToggled', {target: 'document'})
      menuLanguageToggled() {
    
    Tuukka Turu's avatar
    Tuukka Turu committed
        this.closeSearchPanel();
      }
    
    
      // Close the search panel if user opens University main menu
      @Listen('universityMainMenuToggled', {target: 'document'})
      universityMainMenuPanelToggled() {
        this.closeSearchPanel();
      }
    
    
    Tuukka Turu's avatar
    Tuukka Turu committed
      // CLose the search panel on Scroll down.
      @Listen('headerScrollDown', {target: 'document'})
      headerScrollDown() {
        this.closeSearchPanel();
      }
    
      closeSearchPanel() {
    
    druid's avatar
    druid committed
        this.isSearchPanelOpen = false;
    
    Tuukka Turu's avatar
    Tuukka Turu committed
    
        let cludoSayt = document.querySelectorAll('stencil-cludo-sayt')[0] as HTMLElement;
        if (cludoSayt) {
    
          let CludoSayt = window.CludoSayt;
          if (typeof CludoSayt !== 'undefined') {
            CludoSayt.hide();
          }
    
    Tuukka Turu's avatar
    Tuukka Turu committed
        }
    
    
    druid's avatar
    druid committed
        this.showBackdropShadow();
    
    druid's avatar
    druid committed
      }
    
    
    druid's avatar
    druid committed
      handleSearchPanelToggle(event) {
    
    druid's avatar
    druid committed
        this.isSearchPanelOpen = !this.isSearchPanelOpen;
    
        if (this.isSearchPanelOpen) {
          //const searchPanelSelector = this.el as HTMLElement;
    
          // Close desktop menu panel and lang menu panel if they are open.
          this.searchPanelToggled.emit();
    
          let hyHeader = this.el.closest('.hy-site-header') as HTMLElement;
    
    druid's avatar
    druid committed
          const headerHeight = hyHeader.classList.contains('hy-site-header--sticky-active')
            ? `${hyHeader.offsetHeight}px`
            : `${hyHeader.offsetTop + hyHeader.offsetHeight}px`;
    
    druid's avatar
    druid committed
          const searchPanel = this.el.shadowRoot.querySelectorAll(`.site-search__panel`)[0] as HTMLElement;
          searchPanel.style.top = headerHeight;
    
    druid's avatar
    druid committed
    
    
    Tuukka Turu's avatar
    Tuukka Turu committed
          // Remove hidden class from cludo suggestions
          let cludoSayt = document.querySelectorAll('stencil-cludo-sayt')[0] as HTMLElement;
          if (cludoSayt) {
            cludoSayt.classList.remove('hidden');
          }
    
    
    druid's avatar
    druid committed
          // Add shadow backdrop
          let rect = searchPanel.getBoundingClientRect();
    
          let shadowTop = hyHeader.classList.contains('hy-site-header--sticky-active')
            ? hyHeader.offsetHeight + rect.height
            : hyHeader.offsetTop + hyHeader.offsetHeight + rect.height;
          this.showBackdropShadow('open', shadowTop);
    
    
          // Without setTimeout it will not focus the input because it hasn't rendered yet.
          setTimeout(() => {
            const searchInput = this.el.shadowRoot.querySelector('input#search') as HTMLElement;
            searchInput.focus();
          });
    
    druid's avatar
    druid committed
        } else {
    
    Tuukka Turu's avatar
    Tuukka Turu committed
          this.closeSearchPanel();
    
    druid's avatar
    druid committed
        }
    
    druid's avatar
    druid committed
        event.stopPropagation();
    
    druid's avatar
    druid committed
      }
    
    
    druid's avatar
    druid committed
      showBackdropShadow(state = 'close', top = 0) {
        let hyHeader = this.el.closest('.hy-site-header') as HTMLElement;
        let hyBackdropDiv = hyHeader.children[0] as HTMLElement;
    
        if (hyBackdropDiv) {
          if (state === 'open') {
            let me = window.outerHeight - top;
            hyBackdropDiv.style.height = `${me}px`;
            hyBackdropDiv.style.top = `${top}px`;
            hyBackdropDiv.style.position = 'absolute';
            hyBackdropDiv.classList.add('is-active');
          } else {
            hyBackdropDiv.removeAttribute('style');
            hyBackdropDiv.classList.remove('is-active');
          }
        }
      }
    
    
      render() {
        return (
    
    druid's avatar
    druid committed
          <Host
    
    druid's avatar
    druid committed
              'site-search': true,
              'site-search__is-open': this.isSearchPanelOpen,
    
    druid's avatar
    druid committed
            <button
              aria-label={this._labels['open']}
    
              aria-expanded={`${this.isSearchPanelOpen}`}
    
    druid's avatar
    druid committed
              class={{
                'button--search': true,
    
    druid's avatar
    druid committed
                'is-open--menu': this.isAlternative,
                'is-open': this.isSearchPanelOpen,
              }}
    
    druid's avatar
    druid committed
              onClick={(e) => this.handleSearchPanelToggle(e)}
    
    druid's avatar
    druid committed
            >
    
              {this.showLabel ? (
                <span class={{'button--search__label': true, 'button--search__label__group': this.isGroup}}>
                  {this._labels['label']}
                </span>
              ) : (
                ''
              )}
    
    druid's avatar
    druid committed
              <hy-icon icon={'hy-icon-search'} size={this.size} fill={this.color} />
            </button>
            <div
              class={{
                'site-search__panel': true,
                'site-search__panel__is-open': this.isSearchPanelOpen,
              }}
    
              aria-hidden={`${!this.isSearchPanelOpen}`}
    
    druid's avatar
    druid committed
            >
    
    druid's avatar
    druid committed
              <div class="site-search__panel__wrapper">
                <div class="site-search__panel__title">
                  <div class="site-search__panel__title__group">
    
                    <h1>{this.searchTitleLabel}</h1>
                    <div class="description">{this.searchDescriptionLabel}</div>
    
    druid's avatar
    druid committed
                  </div>
    
    druid's avatar
    druid committed
                </div>
    
    Tuukka Turu's avatar
    Tuukka Turu committed
                <div id="cludo-search-results" class="container_12">
                  <div class="search-banner"></div>
                  <div class="search-filters search-filters-mobile"></div>
    
                  <div class="col-md-12">
                    <div style={{margin: '20px 0 32px'}} id="cludo-search-content-form">
                      <hy-search-field input-id="search" label=""></hy-search-field>
                    </div>
                  </div>
    
                  <div class="col-md-3">
                    <div class="search-filters" aria-controls="search-results"></div>
                  </div>
                  <div class="col-md-9">
                    <div class="search-results-container">
                      <hy-paragraph-text>
                        <div class="search-result-count"></div>
                      </hy-paragraph-text>
                      <div class="search-did-you-mean" role="Complementary"></div>
                      <div class="search-results" role="region" id="search-results" aria-live="polite"></div>
                    </div>
                  </div>
    
    druid's avatar
    druid committed
                </div>
    
    Tuukka Turu's avatar
    Tuukka Turu committed
    
    
                {this._searchTools && this._searchTools.length > 0 && (
    
    druid's avatar
    druid committed
                  <div class="site-search__panel__tools">
    
    druid's avatar
    druid committed
                    <div class="title">{this.searchToolsLabel}</div>
                    <div class="list">
                      {this._searchTools.map((i) => {
                        let searchToolTarget = i.isExternal ? '_blank' : '_self';
                        return (
                          <a class="search-special-tool" href={i.url} target={searchToolTarget}>
                            <hy-icon icon={'hy-icon-arrow-to-right'} size={14} fill={ColorVariant.black} />
                            <span class="label">{i.label}</span>
                            <span class="description">{i.description}</span>
                          </a>
                        );
                      })}
                    </div>
    
    druid's avatar
    druid committed
                  </div>
                )}
    
    druid's avatar
    druid committed
                <button
    
    Tuukka Turu's avatar
    Tuukka Turu committed
                  onClick={() => this.closeSearchPanel()}
    
    druid's avatar
    druid committed
                  class={{
                    'site-search__panel__panel-toggle': true,
                  }}
                  aria-label={this.searchCloseLabel}
    
                  aria-expanded={`${this.isSearchPanelOpen}`}
    
    druid's avatar
    druid committed
                >
                  <span class="site-search__panel__panel-toggle__label">
                    <span class="site-search__panel__panel-toggle__label__title" tabindex="0">
                      {this.searchCloseLabel}
                    </span>
                    <hy-icon icon={'hy-icon-remove'} size={16} fill={ColorVariant.black} />
                  </span>
                </button>
    
    druid's avatar
    druid committed
              </div>
    
    druid's avatar
    druid committed
            </div>
          </Host>