export interface SearchTools { label: string; url: string; description: string; menuLinkId: string; isExternal: string; } export interface SearchLabels { label?: string; } 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', shadow: true, }) export class SiteSearch { @Element() el: HTMLElement; @Prop() color: ColorVariant = ColorVariant.black; @Prop() isAlternative: boolean = false; @Prop() labels?: ComponentLabels[] | string; @Prop() searchLabels: string; @Prop() searchTools: string; private _searchTools: SearchTools[]; @Prop() showLabel: boolean = false; @Prop() size: number; @Prop() dataSearchSpecialTools: string; private _searchLabels: SearchLabels[]; private searchTitleLabel: string; private searchCloseLabel: string; private searchToolsLabel: string; private searchDescriptionLabel: string; @State() isSearchPanelOpen: boolean = false; @Event() searchPanelToggled: EventEmitter; private _labels: ComponentLabels[]; @Watch('labels') labelsWatcher(data: ComponentLabels[] | string) { this._labels = typeof data === 'string' ? JSON.parse(data) : data; } 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']; } } componentWillRender() { this.labelsWatcher(this.labels); } // CLose the search panel if user opens the desktop menu panel. @Listen('menuDesktopToggled', {target: 'document'}) desktopMenuToggled() { this.isSearchPanelOpen = false; this.showBackdropShadow(); } // CLose the search panel if user opens the mobile menu panel. @Listen('menuMobileToggled', {target: 'document'}) mobileMenuToggled() { this.isSearchPanelOpen = false; this.showBackdropShadow(); } // CLose the search panel if user opens the language menu. @Listen('menuLanguageToggled', {target: 'document'}) menuLanguageToggled() { this.isSearchPanelOpen = false; this.showBackdropShadow(); } handleSearchPanelToggle(event) { 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; const headerHeight = hyHeader.classList.contains('hy-site-header--sticky-active') ? `${hyHeader.offsetHeight}px` : `${hyHeader.offsetTop + hyHeader.offsetHeight}px`; const searchPanel = this.el.shadowRoot.querySelectorAll(`.site-search__panel`)[0] as HTMLElement; searchPanel.style.top = headerHeight; // 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(); }); } else { this.showBackdropShadow(); } event.stopPropagation(); } 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'); } } } handleSearchPanelClose() { this.isSearchPanelOpen = false; this.showBackdropShadow(); } render() { return ( <Host class={{ 'site-search': true, 'site-search__is-open': this.isSearchPanelOpen, }} > <button aria-label={this._labels['open']} aria-expanded={`${this.isSearchPanelOpen}`} class={{ 'button--search': true, 'is-open--menu': this.isAlternative, 'is-open': this.isSearchPanelOpen, }} onClick={(e) => this.handleSearchPanelToggle(e)} > {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}`} > <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> </div> </div> <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> </div> {this._searchTools && this._searchTools.length > 0 && ( <div class="site-search__panel__tools"> <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> </div> )} <button onClick={() => this.handleSearchPanelClose()} class={{ 'site-search__panel__panel-toggle': true, }} aria-label={this.searchCloseLabel} aria-expanded={`${this.isSearchPanelOpen}`} > <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> </div> </div> </Host> ); } }