diff --git a/src/components.d.ts b/src/components.d.ts index 1c11d510480af026508160bf8b6738086dfe4ca3..0b93845a32f710d98497ee00c0d62c685b40b2f0 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -752,6 +752,7 @@ export namespace Components { } interface HySiteSearch { color: ColorVariant; + dataSearchSpecialTools: string; isAlternative: boolean; labels?: ComponentLabels[] | string; showLabel: boolean; @@ -2060,8 +2061,10 @@ declare namespace LocalJSX { } interface HySiteSearch { color?: ColorVariant; + dataSearchSpecialTools?: string; isAlternative?: boolean; labels?: ComponentLabels[] | string; + onSearchPanelToggled?: (event: CustomEvent<any>) => void; showLabel?: boolean; size?: number; } diff --git a/src/components/hy-search-field/readme.md b/src/components/hy-search-field/readme.md index 1d190e11b37f1ce7ce789e0873c7c6eedf286a2c..6ef6e90806a79d13f9dc27b2f4e2732a9aee4906 100644 --- a/src/components/hy-search-field/readme.md +++ b/src/components/hy-search-field/readme.md @@ -12,6 +12,10 @@ ## Dependencies +### Used by + +- [hy-site-search](../site-header/site-search) + ### Depends on - [hy-icon](../icon) @@ -21,6 +25,7 @@ ```mermaid graph TD; hy-search-field --> hy-icon + hy-site-search --> hy-search-field style hy-search-field fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/src/components/navigation/menu-language/menu-language.tsx b/src/components/navigation/menu-language/menu-language.tsx index 60d8c8f21f6d7fdc861324082f7b34a2b3bedec8..6af56d647fbb051252f2c2293573d683426cfb15 100644 --- a/src/components/navigation/menu-language/menu-language.tsx +++ b/src/components/navigation/menu-language/menu-language.tsx @@ -49,6 +49,12 @@ export class MenuLanguage { this.isMenuOpen = false; } + // CLose the language menu if user opens the search panel + @Listen('searchPanelToggled', {target: 'document'}) + searchPanelToggled() { + this.isMenuOpen = false; + } + @Listen('focus') handleComponentFocus(event) { // Close desktop menu panel if it's open. diff --git a/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.tsx b/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.tsx index 35bd9d81615a0b919596bbf2c0cef6edfc857c06..3ea0d1da7c760d02167463f9b8ffe51233190372 100644 --- a/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.tsx +++ b/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.tsx @@ -231,6 +231,13 @@ export class HyDesktopMenuLinks { this.closePanel(fadeOut); } + // Close the desktop menu panel if user opens search panel + @Listen('searchPanelToggled', {target: 'document'}) + searchPanelToggled() { + let fadeOut = true; + this.closePanel(fadeOut); + } + // CLose the desktop menu panel if user scrolls Sticky Header till the very top. @Listen('headerScrollUp', {target: 'document'}) headerScrollUp() { diff --git a/src/components/site-header/readme.md b/src/components/site-header/readme.md index b1e105e5db393afca0118d046713ce057b6b2504..8bec20a7768721485c2350213124a977d02eb45a 100644 --- a/src/components/site-header/readme.md +++ b/src/components/site-header/readme.md @@ -47,6 +47,8 @@ graph TD; hy-menu-language --> hy-menu-language-item hy-menu-language --> hy-icon hy-site-search --> hy-icon + hy-site-search --> hy-search-field + hy-search-field --> hy-icon style hy-site-header fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/src/components/site-header/site-search/readme.md b/src/components/site-header/site-search/readme.md index 646bcbb7fe47efacdfbea60b34ffe68af79d7bc8..91786f0805a5f46f3b12fd2ce6433dcf0a0fd2db 100644 --- a/src/components/site-header/site-search/readme.md +++ b/src/components/site-header/site-search/readme.md @@ -4,13 +4,20 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| --------------- | ---------------- | ----------- | ------------------------------------------ | -------------------- | -| `color` | `color` | | `ColorVariant.black \| ColorVariant.white` | `ColorVariant.black` | -| `isAlternative` | `is-alternative` | | `boolean` | `false` | -| `labels` | `labels` | | `ComponentLabels[] \| string` | `undefined` | -| `showLabel` | `show-label` | | `boolean` | `false` | -| `size` | `size` | | `number` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ------------------------ | --------------------------- | ----------- | ------------------------------------------ | -------------------- | +| `color` | `color` | | `ColorVariant.black \| ColorVariant.white` | `ColorVariant.black` | +| `dataSearchSpecialTools` | `data-search-special-tools` | | `string` | `undefined` | +| `isAlternative` | `is-alternative` | | `boolean` | `false` | +| `labels` | `labels` | | `ComponentLabels[] \| string` | `undefined` | +| `showLabel` | `show-label` | | `boolean` | `false` | +| `size` | `size` | | `number` | `undefined` | + +## Events + +| Event | Description | Type | +| -------------------- | ----------- | ------------------ | +| `searchPanelToggled` | | `CustomEvent<any>` | ## Dependencies @@ -21,12 +28,15 @@ ### Depends on - [hy-icon](../../icon) +- [hy-search-field](../../hy-search-field) ### Graph ```mermaid graph TD; hy-site-search --> hy-icon + hy-site-search --> hy-search-field + hy-search-field --> hy-icon hy-site-header --> hy-site-search style hy-site-search fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/src/components/site-header/site-search/site-search.scss b/src/components/site-header/site-search/site-search.scss index f9991d1ef717a68f74887c77b025468924fb6e71..1d91d243b64cbe319376d35d60332a2cdeeba64c 100644 --- a/src/components/site-header/site-search/site-search.scss +++ b/src/components/site-header/site-search/site-search.scss @@ -83,3 +83,30 @@ } } } + +.site-search { + &__panel { + display: none; + &__is-open { + background-color: var(--grayscale-white); + border: 1px solid rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + left: 0; + position: absolute; + width: 100%; + z-index: 520; + } + + &__title { + background-color: #f8f8f8; + display: flex; + flex-direction: row; + justify-content: space-between; + .label { + } + .site-search__panel__panel-toggle { + } + } + } +} diff --git a/src/components/site-header/site-search/site-search.tsx b/src/components/site-header/site-search/site-search.tsx index 14ed05b7e96c20331d6a12eaf024edd5469835fd..fa498d5aa6d9ef8f6e922edec649086b6dbbe18c 100644 --- a/src/components/site-header/site-search/site-search.tsx +++ b/src/components/site-header/site-search/site-search.tsx @@ -1,4 +1,9 @@ -import {Component, Prop, h, Watch} from '@stencil/core'; +export interface SearchSpecialTools { + url: string; + 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'; @@ -8,33 +13,145 @@ import {ColorVariant} from '../../../utils/utils'; shadow: true, }) export class SiteSearch { + @Element() el: HTMLElement; + @Prop() color: ColorVariant = ColorVariant.black; @Prop() isAlternative: boolean = false; @Prop() labels?: ComponentLabels[] | string; @Prop() showLabel: boolean = false; @Prop() size: number; + @Prop() dataSearchSpecialTools: string; + private _dataSearchSpecialTools: SearchSpecialTools[]; + + @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() { + if (this.dataSearchSpecialTools) { + this._dataSearchSpecialTools = JSON.parse(this.dataSearchSpecialTools); + } + } + componentWillRender() { this.labelsWatcher(this.labels); } + // CLose the search panel if user opens the desktop menu panel. + @Listen('menuDesktopToggled', {target: 'document'}) + desktopMenuToggled() { + this.isSearchPanelOpen = false; + } + + // CLose the search panel if user opens the language menu. + @Listen('menuLanguageToggled', {target: 'document'}) + menuLanguageToggled() { + this.isSearchPanelOpen = false; + } + + // Close the search panel if user clicks anywhere outside the Search component. + @Listen('click', {target: 'window'}) + handleWindowClick(event) { + if (event.target.tagName.toLowerCase() !== 'hy-site-search') { + this.isSearchPanelOpen = false; + } + event.stopPropagation(); + } + + handleSearchPanelToggle() { + 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; + let rectHeader = hyHeader.getBoundingClientRect(); + + const headerHeight = `${rectHeader.bottom}px`; + const searchPanel = this.el.shadowRoot.querySelectorAll(`.site-search__panel`)[0] as HTMLElement; + searchPanel.style.top = headerHeight; + } + //event.stopPropagation(); + } + + handleSearchPanelClose() { + this.isSearchPanelOpen = false; + } + render() { return ( - <button - aria-label={this._labels['open']} + <Host class={{ - 'button--search': true, - 'is-open--menu': this.isAlternative, + 'site-search': true, + 'site-search__is-open': this.isSearchPanelOpen, }} > - {this.showLabel ? <span class={'button--search__label'}>{this._labels['label']}</span> : ''} - <hy-icon icon={'hy-icon-search'} size={this.size} fill={this.color} /> - </button> + <button + aria-label={this._labels['open']} + class={{ + 'button--search': true, + 'is-open--menu': this.isAlternative, + 'is-open': this.isSearchPanelOpen, + }} + onClick={() => this.handleSearchPanelToggle()} + > + {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, + }} + > + <div class="site-search__panel__title"> + <div class="site-search__panel__title__label"> + <h1>Search</h1> + <div>web pages, study options, people, research groups, etc…</div> + </div> + <button + onClick={() => this.handleSearchPanelClose()} + class={{ + 'site-search__panel__panel-toggle': true, + }} + aria-label="Exit Search" + > + <span class="site-search__panel__panel-toggle__label"> + <span class="site-search__panel__panel-toggle__label__title">Exit Search</span> + <hy-icon icon={'hy-icon-remove'} size={20} fill={ColorVariant.black} /> + </span> + </button> + </div> + <div class="site-search__panel__input"> + <hy-search-field input-id="search" label="Search degree programmes" /> + </div> + <div class="site-search__panel__results"> + <div class="filters"></div> + <div class="results"></div> + </div> + {this.dataSearchSpecialTools && this._dataSearchSpecialTools.length > 0 && ( + <div class="site-search__panel__tools"> + <div>Special Search tools</div> + {this._dataSearchSpecialTools.map((i) => { + return ( + <a class={'search-special-tool'} href={i.url}> + <hy-icon icon={'hy-icon-arrow-to-right'} size={14} fill={ColorVariant.black} /> + <span class={'search-special-tool__label'}>{i.label}</span> + </a> + ); + })} + </div> + )} + </div> + </Host> ); } }