/* eslint-disable ember/avoid-leaking-state-in-ember-objects */
/* eslint-disable ember/use-ember-get-and-set */
import Component from "@ember/component";
import { computed } from "@ember/object";
import { isEmpty } from "@ember/utils";
import $ from "jquery";
import { scheduleOnce } from "@ember/runloop";
import { translationMacro as t } from 'ember-i18n';
export default Component.extend({
/**
Array of search objects.
@property sitemap
@type Array
*/
sitemap: [],
/**
Result array consisting of filtered objects.
@property _results
@type Array
@private
*/
_results: [],
/**
User input from .sitemap-search-input.
@property lastKeyPress
@type string
*/
userQuery: null,
/**
Toggler for showing .sitemap-search-results-list.
@property isShowingResults
@type boolean
*/
isShowingResults: false,
/**
Default class for component wrapper.
@property classNames
@type Array
@readOnly
*/
classNames: ['sitemap-searchbar', 'ui', 'search'],
/**
Flag for showing error message if user query doesn't get any hits.
@property noHits
@type boolean
*/
noHits: computed('_results', 'userQuery', function() {
const results = this.get('_results');
if (results) {
return results.length === 0 && !isEmpty(this.get('userQuery'));
}
return undefined;
}),
/**
Event timestamp in milliseconds.
@property lastKeyPress
@type number
*/
lastKeyPress: 0,
/**
Component's input placeholder.
@property placeholder
@type String
@default t('components.flexberry-sitemap-searchbar.placeholder')
*/
placeholder: t('components.flexberry-sitemap-searchbar.placeholder'),
/**
* Recursively returns filtered sitemap.
*
* @param regexQuery
* @param currentTree
* @private
* @function _searchTree
*/
_searchTree(regexQuery, currentTree) {
let resultTree = [];
currentTree.forEach(element => {
if (this._elementMatchesRegex(regexQuery, element)) {
let matchingNode = $.extend(true, {}, element);
resultTree.push(matchingNode);
} else if (this._elementHasChildren(element)) {
let resultChildren = this._searchTree(regexQuery, element.children);
if (resultChildren.length > 0) {
const { link, caption, title } = element;
resultTree.push({ link, caption, title, children: resultChildren });
}
}
});
return resultTree;
},
/**
* Checks element caption string for regex.
*
* @param regex
* @param element
* @private
* @function _elementMatchesRegex
*/
_elementMatchesRegex(regex, element) {
if (element.caption) {
return regex.test(element.caption.string);
} else {
return regex.test(element.title.string);
}
},
/**
* Checks if element has children.
*
* @param element
* @private
* @function _elementHasChildren
*/
_elementHasChildren(element) {
return (typeof element === 'object') && (!isEmpty(element.children));
},
/**
* Runs search.
*
* @private
* @function _startSearch
*/
_startSearch() {
let query = this.get('userQuery').toLowerCase();
let regexQuery = new RegExp(`${query}`, 'gi');
this.set('_results', this._searchTree(regexQuery, this.get('sitemap')));
},
actions: {
/**
Initiate sitemap search.
@private
@function startSearch
*/
startSearch() {
this.set('isShowingResults', true);
let query = this.get('userQuery').toLowerCase();
if (query) {
// Recursive search will be initiated only if last keypress happened more than 200 ms ago (for performance reasons).
scheduleOnce('afterRender', this, this._startSearch);
} else {
this.set('_results', this.get('sitemap'));
}
},
/**
Toggle isShowingResults prop.
@private
@function toggleResultsList
*/
toggleResultsList() {
if (!$('.icon-guideline-search').hasClass('visible')) {
this.toggleProperty('isShowingResults');
}
},
/**
Click on the search icon handler.
@private
@function clickSearchIcon
*/
clickSearchIcon() {
this.toggleProperty('isShowingResults');
$('.sitemap-search-input').toggleClass('hidden');
}
},
/**
Initializes DOM-related component's logic.
*/
didInsertElement() {
this.set('_results', this.get('sitemap'));
this.$(document).on('click', e => {
e.stopPropagation();
try {
let clickTargetIsNotComponent = !e.target.offsetParent.classList.contains('sitemap-search-results-list') &&
!e.target.classList.contains('sitemap-search-results-list') &&
!e.target.classList.contains('sitemap-search-input') &&
!e.target.classList.contains('sitemap-search-icon') &&
!e.target.classList.contains('sitemap-search-input');
if (clickTargetIsNotComponent) {
this.set('isShowingResults', false);
if ($('.ui.sidebar.main.menu').hasClass('sidebar-mini')) {
$('.sitemap-search-input').addClass('hidden');
}
}
} catch (err) {
return;
}
});
}
});