import jQ from 'jquery';
import BaseComponent from '../../base-component';
import SearchInput from '../instant-search-element/search-input';
import Class from "../../../helpers/class";
import Settings from "../../../helpers/settings";
import Labels from "../../../helpers/labels";
import Selector from "../../../helpers/selector";
import Utils from "../../../helpers/utils";
import Globals from '../../../helpers/globals';
/**
* Instant search on mobile
* @extends BaseComponent
*/
class InstantSearchMobile extends BaseComponent {
/**
* @constructs
*/
constructor() {
super();
this.data = '';
this.isBoundEvents = false;
this.isOpen = false;
this.inputMobileId = Selector.searchBoxMobile.substr(1);
this.searchBox = null;
this.selector = {
searchInput: Selector.searchBoxMobile,
clearButton: '.' + Class.searchSuggestionBtnClearMobile,
closebutton: '.' + Class.searchSuggestionBtnCloseMobile,
submitButton: '.' + Class.searchSuggestionBtnSubmitMobile,
topPanel: '.' + Class.searchSuggestion + '-mobile-top-panel',
overlay: '.' + Class.searchSuggestion + '-mobile-overlay',
searchInputs: 'input[name="' + Settings.getSettingValue('search.termKey') + '"]'
};
}
/**
* Enum of Insatant search mobile templates (SEARCH_BTN, DEFAULT)
* @enum {Object}
*/
static get tempType() {
return {
SEARCH_BTN: 'search_btn',
DEFAULT: 'default'
};
}
/**
* Returns whether or not this instant search result mobile is used
*/
static isActive() {
return true;
}
/**
* Get the raw HTML template of Search box on mobile
* @param {String} tempType The template type: 'default' or 'search_btn'
* @returns {String} The raw HTML template
*/
getTemplate(tempType) {
switch (tempType) {
case InstantSearchMobile.tempType.SEARCH_BTN:
return `
<a href="javascript:;" class="{{class.searchSuggestionBtnSubmitMobile}}"><span>Submit</span></a>
`;
default:
return `
<div class="{{class.searchSuggestion}}-mobile-overlay"></div>
<div class="{{class.searchSuggestion}}-mobile-top-panel">
<form action="/search" method="get">
<button type="button" class="{{class.searchSuggestionBtnCloseMobile}}"><-</button>
{{btnSearch}}
<input type="text" name="{{searchTermKey}}" placeholder="{{searchBoxPlaceholder}}" id="{{searchId}}" />
<button type="button" class="{{class.searchSuggestionBtnClearMobile}}">X</button>
</form>
</div>
`;
}
}
/**
* Replace the brackets in raw html template with proper values
* @returns {String} HTML string
*/
compileTemplate() {
var submitBtn = '';
// Show search button on mobile OR NOT
if (Settings.getSettingValue('search.showSearchBtnMobile')) {
submitBtn = this.getTemplate(InstantSearchMobile.tempType.SEARCH_BTN);
}
return this.getTemplate()
.replace(/{{btnSearch}}/g, submitBtn)
.replace(/{{searchTermKey}}/g, Settings.getSettingValue('search.termKey'))
.replace(/{{searchBoxPlaceholder}}/g, Labels.suggestion.searchBoxPlaceholder)
.replace(/{{searchId}}/g, this.inputMobileId)
.replace(/{{class.searchSuggestion}}/g, Class.searchSuggestion)
.replace(/{{class.searchSuggestionBtnSubmitMobile}}/g, Class.searchSuggestionBtnSubmitMobile)
.replace(/{{class.searchSuggestionBtnCloseMobile}}/g, Class.searchSuggestionBtnCloseMobile)
.replace(/{{class.searchSuggestionBtnClearMobile}}/g, Class.searchSuggestionBtnClearMobile);
}
/**
* Render the search box on mobile
*/
render() {
jQ('body').append(this.compileTemplate());
}
/**
* Returns whether or not the events are bind on instant search mobile
*/
isBindEvents() {
return !this.isBoundEvents;
};
/**
* Bind the events on Instant search mobile
*/
bindEvents() {
this.$searchInput = jQ(this.selector.searchInput);
this.$clearButtonElement = jQ(this.selector.clearButton);
this.$closebuttonElement = jQ(this.selector.closebutton);
this.$submitButtonElement = jQ(this.selector.submitButton);
this.$topPanelElement = jQ(this.selector.topPanel);
this.$overlayElement = jQ(this.selector.overlay);
// apply autocomplete for search input
this.searchBox = new SearchInput(this.inputMobileId, this.$searchInput);
this.searchBox.refresh();
// Add close button event
this.$closebuttonElement.on('click', this.closeInstantSearchMobile.bind(this, true));
// Add clear button event
this.$clearButtonElement.on('click', this.clearInstantSearchMobile.bind(this));
this.$searchInputs = jQ(this.selector.searchInputs);
this.$searchInputs
.on('click', this._onClickSearchBox.bind(this))
.on('focus', this._onFocusSearchBox.bind(this))
.on('keyup', this._onTypeSearchBoxEvent.bind(this));
this.$targetInput = null;
this.isBoundEvents = true;
}
/**
* Bind the click evnet on the search input to open the search box on mobile
* @param {Event} event DOM event
*/
_onClickSearchBox(event) {
if (Utils.isFullWidthMobile()) {
// Set search term for all search input
if (this.$targetInput && this.$targetInput.val() !== '') {
this.$searchInputs.val(this.$targetInput.val());
}
// Open search result box if the exist the search term
if (this.$searchInput && this.$searchInput.val() !== '') {
this.openSuggestionMobile();
}
}
}
/**
* Bind the focus event on the search input to open the search box on mobile
* @param {Event} event DOM Event
*/
_onFocusSearchBox(event) {
if (Utils.isFullWidthMobile()) {
// Open the instant search box when focus on the theme search input
if (!jQ(event.target).is(this.$searchInput)) {
this.$targetInput = jQ(event.target);
this.showSearchBoxMobile();
this.$searchInput.trigger('click');
}
}
}
/**
* Bind the typeahead event on the search input
* @param {*} event
*/
_onTypeSearchBoxEvent(event) {
if (Utils.InstantSearch.isFullWidthMobile()) {
this.searchBox.instantSearchResult.$wrapper.show();
if (event.target.value == '') {
this.closeInstantSearchMobile();
this.$clearButtonElement.hide();
} else {
this.$clearButtonElement.show();
}
}
}
/**
* Show search box in mobile
*/
showSearchBoxMobile() {
this.isOpen = true;
// Click outside suggestion and close the suggestion
this.onClickOutsideSuggestionMobileEvent();
// Close keyboard when scrolling on mobile
this.scrollSuggestionMobileEvent();
// Display or not the clear button
if (this.$searchInput.val() == '') {
this.$clearButtonElement.hide();
} else {
this.$clearButtonElement.show();
}
if (!this.$searchInput.is(':focus')) {
// Display the Search box on Top
this.$topPanelElement.show();
this.$overlayElement.show();
// Remove Lock focus on Menu mobile, Dialog,... which have tabindex=-1
jQ('[tabindex=-1]').removeAttr('tabindex').addClass(Class.searchSuggestionNoTabIndex);
if (Utils.isMobile() && jQ("[data-open=true]").length > 0) {
jQ("[data-open=true]").attr('data-open', false);
}
// Focus Search box
setTimeout(() => {
this.$searchInput.focus();
}, 100);
// After showing the Search box on top on mobile
this.afterShowSearchBoxMobile();
}
}
/**
* Close search box on mobile
* @param {Boolean} isClose Close the search box or just close the instant search result
*/
closeInstantSearchMobile(isClose) {
// Close Suggestion box
this.$searchInput.autocomplete('close');
this.searchBox.instantSearchResult.$wrapper.hide()
// Remove search box
var isClose = typeof isClose !== 'undefined' ? isClose : false;
if (isClose) {
this.$topPanelElement.hide();
this.$overlayElement.hide();
}
// Update current term for all search boxes
this._setValueAllSearchBoxes();
// Add back tabindex=-1
jQ('.' + Class.searchSuggestionNoTabIndex).attr('tabindex', -1);
// Return scrolling of body
if (jQ('body').hasClass(Class.searchSuggestionMobileOpen)) {
jQ('body').removeClass(Class.searchSuggestionMobileOpen);
}
this.afterCloseInstantSearchMobile(isClose);
}
/**
* Clear the current search term and instant search result
*/
clearInstantSearchMobile() {
this.$clearButtonElement.hide();
// Set blank for current term
Globals.currentTerm = '';
// Apply blank for all search boxes
this._setValueAllSearchBoxes();
// Close Instant search result
this.closeInstantSearchMobile();
// Focus again Search box
this.$searchInput.focus();
}
/**
* After close the instant search mobile
* @param {String} isClose Close the search box or just close the instant search result
*/
afterCloseInstantSearchMobile(isClose) {
// Overide this method for customization
}
/**
* Update the current search term to all search inputs
* @param {*} value
*/
_setValueAllSearchBoxes(value) {
var value = typeof value !== 'undefined' ? searchTerm : Globals.currentTerm;
this.$searchInputs.val(value);
}
/**
* Bind the click event on outside of instant search mobile to close it.
*/
onClickOutsideSuggestionMobileEvent() {
jQ(document).on('touchstart', (event) => {
var contentContainer = jQ('.' + Class.searchSuggestion + '-mobile-top-panel');
if (!contentContainer.is(event.target)
&& contentContainer.has(event.target).length == 0
&& this.searchBox
&& this.searchBox.instantSearchResult.$wrapper.has(event.target).length == 0) {
this.closeInstantSearchMobile(true);
}
});
}
/**
* Bind the scroll event on instant search mobile
*/
scrollSuggestionMobileEvent() {
jQ(document).on('touchmove', (e) => {
if (this.$searchInput.is(':focus')) {
this.$searchInput.blur();
}
});
}
/**
* After show the instant search mobile
*/
afterShowSearchBoxMobile() {
// Override this method for customization
}
/**
* Open the instant search mobile
*/
openSuggestionMobile() {
// Before opening suggestion
this.beforeOpenSuggestionMobile();
// Add a specific class to prevent body from scrolling
if (!jQ('body').hasClass(Class.searchSuggestionMobileOpen)) {
jQ('body').addClass(Class.searchSuggestionMobileOpen);
}
// Dsisplay the Search box on Top
this.showSearchBoxMobile();
this.$searchInput.autocomplete('search');
this.searchBox.instantSearchResult.$wrapper.show();
// Scroll to the top when clicking to the search box
jQ('html,body').scrollTop();
// After opening suggestion
this.afterOpenSuggestionMobile();
}
beforeOpenSuggestionMobile() {
// Override this method for customization
}
afterOpenSuggestionMobile() {
// Override this method for customization
}
}
export default InstantSearchMobile;