Skip to content
Snippets Groups Projects
site-search.tsx 7.59 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
    }
    
    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;
    
    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() {
        this.isSearchPanelOpen = false;
    
    druid's avatar
    druid committed
        this.showBackdropShadow();
      }
    
      // CLose the search panel if user opens the mobile menu panel.
      @Listen('menuMobileToggled', {target: 'document'})
      mobileMenuToggled() {
        this.isSearchPanelOpen = false;
        this.showBackdropShadow();
    
    druid's avatar
    druid committed
      }
    
      // CLose the search panel if user opens the language menu.
      @Listen('menuLanguageToggled', {target: 'document'})
      menuLanguageToggled() {
        this.isSearchPanelOpen = false;
    
    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
    
    
    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 {
          this.showBackdropShadow();
    
    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');
          }
        }
      }
    
    
    druid's avatar
    druid committed
      handleSearchPanelClose() {
        this.isSearchPanelOpen = false;
    
    druid's avatar
    druid committed
        this.showBackdropShadow();
    
    druid's avatar
    druid committed
      }
    
    
      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,
                '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'}>{this._labels['label']}</span> : ''}
              <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>
    
    druid's avatar
    druid committed
                <div class="site-search__panel__input">
                  <hy-search-field input-id="search" />
                </div>
                <div class="site-search__panel__results">
                  <div class="filters"></div>
                  <div class="results"></div>
    
    druid's avatar
    druid committed
                </div>
    
                {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
                  onClick={() => this.handleSearchPanelClose()}
                  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>