SEARCH-OVERLAY (Globale Elemente )

src/app/core/components/search-overlay/templates

Demo Section

Each variation will be presented in the following section.

Default

Wonach suchst du?


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


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://via.placeholder.com/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://via.placeholder.com/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://via.placeholder.com/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://via.placeholder.com/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://via.placeholder.com/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://via.placeholder.com/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>

Wonach suchst du?