SEARCH-OVERLAY (Globale Elemente )
src/app/core/components/search-overlay/templates
Demo Section
Each variation will be presented in the following section.
Default
Readme
searchOverlay (coreComponent
)
Description
Search overlay is a pop up that allows the user to search custom keywords through the Google Custom Search API.
JIRA
Requirements
- @veams/core - Veams Core Framework.
- @veams/query or
jquery
- Veams Query or jQuery. - @veams/component - Veams Component.
- @veams/component-figure - Figure component in Veams.
Fields
search-overlay.hbs
Settings
Parameter | Type | Default | Description |
---|---|---|---|
settings.skiplinksClasses | String | '' |
Modifier classes for component |
settings.searchOverlayJsOptions | Object | null |
JavaScript options which gets stringified |
Content
Parameter | Type | Description |
---|---|---|
content.title | String | Title of the search overlay component |
content.teasers | Array | Array of teaser components embedded in search overlay component |
search-overlay__teaser.hbs
Content
Parameter | Type | Description |
---|---|---|
content.link | Object | Link component of the search overlay component |
content.figure | Object | Figure component of the search overlay component |
JavaScript Options
The module gives you the possibility to override default options:
Option | Type | Default | Description |
---|---|---|---|
classes.active | String | 'is-active' |
Active class to display or hide search overlay component |
classes.notScrollable | String | 'isnt-scrollable' |
Class that disables scrolling on the body |
selectors.inputDesktop | String | '#gsc-i-id2' |
Select gcse input field for desktop |
selectors.inputMobile | String | '#gsc-i-id1' |
Select gcse input field for mobile |
selectors.toggleSearch | String | '[data-js-item="toggle-search"]' |
Select toggle search button |
Templates
search-overlay.hbs
<div class="c-search-overlay{{#if settings.searchOverlayClasses}} {{settings.searchOverlayClasses}}{{/if}}"
data-js-module="search-overlay">
{{#wrapWith "grid__container"}}
{{#wrapWith "grid__row"}}
<div class="search-overlay__wrapper">
<h3 class="search-overlay__title">{{content.title}}</h3>
{{#with @root.search-enter.variations.default}}
{{> search-enter }}
{{/with}}
<ul class="search-overlay__teaser-list">
{{#each content.teasers}}
{{> search-overlay__teaser}}
{{/each}}
</ul>
<button class="search-overlay__close" type="button"
data-js-item="search-overlay-close">{{content.closeBtnLabel}}</button>
</div>
{{/wrapWith}}
{{/wrapWith}}
</div>
search-overlay__teaser.hbs
<li class="search-overlay__teaser{{#if content.link.settings.linkClasses}}{{content.link.settings.linkClasses}}{{/if}}">
<a class="search-overlay__teaser-link" href="{{content.link.content.linkHref}}"{{#if content.link.content.linkTitle}} title="content.link.content.linkTitle}}"{{/if}}>
{{#with content.figure}}
{{> figure}}
{{/with}}
<span class="search-overlay__teaser-title">{{content.link.content.linkText}}</span>
</a>
</li>
Data File
search-overlay.hjson
{
"variations": {
"default": {
"docs": {
"variationName": "Default"
},
"settings": {
"searchOverlayClasses": false
},
"content": {
"title": "Wonach suchst du?",
"teasers": [
{
"settings": {},
"content": {
"figure": {
"settings": {
"figureContextClass": "search-overlay-teaser"
},
"content": {
"figurePicture": {
"settings": {
"pictureContextClass": "search-overlay-teaser",
"pictureClasses": false,
"lazyload": true,
"autoSizes": true
},
"content": {
"fallbackSrc": "https://placehold.co/206x206",
"alt": "Alternative text that describes the image"
}
}
}
},
"link": {
"settings": {
"linkClasses": false
},
"content": {
"linkText": "Studium",
"linkHref": "#",
"linkTitle": false
}
}
}
},
{
"settings": {},
"content": {
"figure": {
"settings": {
"figureContextClass": "search-overlay-teaser"
},
"content": {
"figurePicture": {
"settings": {
"pictureContextClass": "search-overlay-teaser",
"pictureClasses": false,
"lazyload": true,
"autoSizes": true
},
"content": {
"fallbackSrc": "https://placehold.co/206x206",
"alt": "Alternative text that describes the image"
}
}
}
},
"link": {
"settings": {
"linkClasses": false
},
"content": {
"linkText": "Immatrikulation",
"linkHref": "#",
"linkTitle": false
}
}
}
},
{
"settings": {},
"content": {
"figure": {
"settings": {
"figureContextClass": "search-overlay-teaser"
},
"content": {
"figurePicture": {
"settings": {
"pictureContextClass": "search-overlay-teaser",
"pictureClasses": false,
"lazyload": true,
"autoSizes": true
},
"content": {
"fallbackSrc": "https://placehold.co/206x206",
"alt": "Alternative text that describes the image"
}
}
}
},
"link": {
"settings": {
"linkClasses": false
},
"content": {
"linkText": "Stellenangebote",
"linkHref": "#",
"linkTitle": false
}
}
}
}
],
"closeBtnLabel": "Schließen",
"resultsUrl": "http://www.google.com/search"
}
}
}
}
Styles
search-overlay.scss
/* ===================================================
coreComponent: search-overlay
=================================================== */
.c-search-overlay {
display: none;
position: fixed;
background-color: $color-light;
width: 100%;
top: 72px;
padding: 50px 0 50px;
transform: translate3d(110%, 0, 0);
transition: transform $animation-duration-std*4.5 $animation-easing-std;
pointer-events: none;
z-index: 997;
@include bp($bp-tablet-p) {
padding: 70px 0 70px;
}
@include bp($bp-tablet-l) {
display: block;
}
/*
MODIFIERS
----------------------- */
&.is-active {
transform: translate3d(0, 0, 0);
pointer-events: all;
display: block;
position: absolute;
@include bp($bp-tablet-l) {
position: fixed;
}
}
.search-overlay__wrapper {
max-width: 800px;
margin: 0 auto;
}
.search-overlay__title {
@include headline-h2-styles();
}
.search-overlay__teaser-list {
display: flex;
justify-content: space-between;
margin-top: 80px;
}
.search-overlay__teaser {
flex: 0 1 28%;
&:hover,
&.a11y-focus-key {
.search-overlay__teaser-title {
&::before {
left: 5px;
}
}
}
}
.search-overlay__teaser-link {
display: block;
text-decoration: none;
}
.search-overlay__teaser-title {
color: $color-green;
font-size: 1.6rem;
line-height: (24/16);
font-family: $font-bold;
letter-spacing: .3px;
padding-left: 25px;
position: relative;
&::before {
@include pseudo();
@include sprites-icon-arrow-green100();
@include centering(v);
display: block;
left: 0;
transition: left $animation-duration-std $animation-easing-std;
margin-top: -1px;
}
}
.search-overlay__close {
margin-top: 50px;
@include reset-btn();
font-family: $font-bold;
font-size: 2.2rem;
cursor: pointer;
text-align: center;
height: 60px;
padding: 1rem 2rem;
white-space: nowrap;
background-color: $color-green;
color: $color-white;
border: 1px solid $color-light;
&:hover {
background-color: $color-white;
border: 1px solid $color-green;
color: $color-green;
&::before {
@include sprites-icon-close-green100;
}
}
&::before {
content: "";
display: inline-block;
@include sprites-icon-close-white;
color: $color-green;
margin-right: 1rem;
}
@include bp($bp-desktop-l) {
@include visuallyhidden();
}
&:not(.a11y-focus-key) {
@include visuallyhidden();
}
}
}
Scripts
search-overlay.js
/**
* SearchOverlay is a popup that enables users to search custom keywords through the Google Custom Search API.
* Class properties and decorators are supported.
*
* @module SearchOverlay
* @version v1.0.0
*
* @author Oliver Broad
*/
// Imports
import $ from 'jquery';
import Component from '@veams/component';
import FocusTrap from '../../../../shared/utilities/focus-trap/scripts/focus-trap';
class SearchOverlay extends Component {
/**
* Class Properties
*/
$el = $(this.el);
/**
* Event & subscribe handling
*/
// Local Handlers
get events() {
return {
'{{context.EVENTS.click}} {{this.options.selectors.closeBtn}}': 'handleSearchToggle'
};
}
// Global Handlers
get subscribe() {
return {
'{{context.EVENTS.searchOverlay.toggle}}': 'handleSearchToggle'
};
}
/**
* Constructor for our class
*
* @see module.js
*
* @param {Object} obj - Object which is passed to our class
* @param {Object} obj.el - element which will be saved in this.el
* @param {Object} obj.options - options which will be passed in as JSON object
*/
constructor(obj) {
let options = {
selectors: {
closeBtn: '[data-js-item="search-overlay-close"]',
inputDesktop: '[data-js-item="search-enter-field"]',
toggleSearch: '[data-js-item="toggle-search"]'
},
classes: {
active: 'is-active',
notScrollable: 'isnt-scrollable'
},
inputMobile: []
};
super(obj, options);
}
/**
* Handle resize event by closing the component
*/
handleResize() {
this.$inputMobile[0].value = '';
this.$inputMobile[0].blur();
window.scrollTo(0, 0);
this.close();
}
/**
* Handle search toggle event by toggling search overlay
*/
handleSearchToggle() {
let activeClass = this.options.classes.active;
if (this.$el.hasClass(activeClass)) {
this.close();
} else {
this.open();
}
}
/**
* Open search overlay and focus input
*/
open() {
let activeClass = this.options.classes.active;
let notScrollableClass = this.options.classes.notScrollable;
this.$body.addClass(notScrollableClass);
this.$el.addClass(activeClass);
this.$toggleSearch.addClass(activeClass);
this.$toggleSearchContainer.addClass(activeClass);
this.focusTrap.activate();
this.focusSearchField();
}
/**
* Close search overlay
*/
close() {
let activeClass = this.options.classes.active;
let notScrollableClass = this.options.classes.notScrollable;
this.$body.removeClass(notScrollableClass);
this.$el.removeClass(activeClass);
this.$toggleSearch.removeClass(activeClass)[0].focus();
this.$toggleSearchContainer.removeClass(activeClass);
this.focusTrap.deactivate();
}
/**
* Set focus in search field
*/
focusSearchField() {
let activeClass = this.options.classes.active;
if (!this.$el.hasClass(activeClass)) {
this.$el.addClass(activeClass);
}
this.$inputDesktop[0].focus();
}
/**
* Life cycle hook
*/
didMount() {
this.$body = $('body');
this.$toggleSearch = $(this.options.selectors.toggleSearch);
this.focusTrap = new FocusTrap({
el: this.el,
namespace: 'FocusTrap',
context: this.context
});
this.focusTrap.create();
this.focusTrap.didMount();
if (!this.$toggleSearch.length) {
console.warn(
`SearchOverlay::didMount() - ${
this.options.selectors.toggleSearch
} not found in page!`
);
this.destroy();
return;
}
this.$toggleSearchContainer = $(this.$toggleSearch[0].parentNode);
this.$inputDesktop = $(this.options.selectors.inputDesktop);
this.$inputMobile = $(this.options.selectors.inputMobile);
window.addEventListener('orientationchange', () => {
this.handleResize();
});
}
/**
* Render class
*/
render() {
return this;
}
}
export default SearchOverlay;
HTML Output
Default
<div class="c-search-overlay" data-js-module="search-overlay">
<div class="grid__container">
<div class="grid__row">
<div class="search-overlay__wrapper">
<h3 class="search-overlay__title">Wonach suchst du?</h3>
<div class="c-search-enter--overlay" data-css="c-search-enter">
<form class="search-enter__form" action="#">
<div class="c-search-option--default" data-css="c-search-option" data-js-module="search-option" data-js-options='{}'>
<div class="form__radio" data-css="c-form">
<div class="form__radio-wrapper">
<ul class="form__radio-list">
<li class="form__radio-item">
<input id="gsearch-1" name="gsearch-t" type="radio" value="gsearch-1" checked="checked" class="form__radio-input" />
<label role="button" for="gsearch-1" class="form__radio-label">
in LMU.de
</label>
</li>
<li class="form__radio-item">
<input id="gsearch-2" name="gsearch-t" type="radio" value="gsearch-2" class="form__radio-input" />
<label role="button" for="gsearch-2" class="form__radio-label">
in allen LMU-Webseiten
</label>
</li>
</ul>
</div>
</div>
</div>
<div class="search-enter__input-group-wrapper">
<input name="q" data-js-item="search-enter-field" type="text" placeholder="Suchbegriff eingeben" aria-label="Suchbegriff eingeben" class="search-enter__input-text" />
<div class="search-enter__button-area">
<button type="submit" title="Suchen" aria-label="Suchen" class="search-enter__button">
</button>
</div>
</div>
<div class="search-enter__dsgvo-area">
<p><a href="#" class="search-enter__dsgvo-link" target="_blank">Datenschutzhinweis mit Link</a></p>
</div>
</form>
</div>
<ul class="search-overlay__teaser-list">
<li class="search-overlay__teaser">
<a class="search-overlay__teaser-link" href="#">
<figure class="c-figure--search-overlay-teaser" data-css="c-figure">
<div class="figure__wrapper">
<picture class="c-picture--search-overlay-teaser
lazyload" data-css="c-picture">
<!--[if IE 9]><audio><![endif]-->
<!--[if IE 9]></audio><![endif]-->
<img class="
picture__image lazyload" src="https://placehold.co/206x206" alt="Alternative text that describes the image" title="Alternative text that describes the image" data-sizes="auto" />
</picture>
</div>
</figure>
<span class="search-overlay__teaser-title">Studium</span>
</a>
</li>
<li class="search-overlay__teaser">
<a class="search-overlay__teaser-link" href="#">
<figure class="c-figure--search-overlay-teaser" data-css="c-figure">
<div class="figure__wrapper">
<picture class="c-picture--search-overlay-teaser
lazyload" data-css="c-picture">
<!--[if IE 9]><audio><![endif]-->
<!--[if IE 9]></audio><![endif]-->
<img class="
picture__image lazyload" src="https://placehold.co/206x206" alt="Alternative text that describes the image" title="Alternative text that describes the image" data-sizes="auto" />
</picture>
</div>
</figure>
<span class="search-overlay__teaser-title">Immatrikulation</span>
</a>
</li>
<li class="search-overlay__teaser">
<a class="search-overlay__teaser-link" href="#">
<figure class="c-figure--search-overlay-teaser" data-css="c-figure">
<div class="figure__wrapper">
<picture class="c-picture--search-overlay-teaser
lazyload" data-css="c-picture">
<!--[if IE 9]><audio><![endif]-->
<!--[if IE 9]></audio><![endif]-->
<img class="
picture__image lazyload" src="https://placehold.co/206x206" alt="Alternative text that describes the image" title="Alternative text that describes the image" data-sizes="auto" />
</picture>
</div>
</figure>
<span class="search-overlay__teaser-title">Stellenangebote</span>
</a>
</li>
</ul>
<button class="search-overlay__close" type="button" data-js-item="search-overlay-close">Schließen</button>
</div>
</div>
</div>
</div>