Source: components/filter/filter-tree/filter-option-element/filter-search-box.js

import jQ from 'jquery';

import BaseComponent from "../../../base-component";
import Class from "../../../../helpers/class";
import Labels from "../../../../helpers/labels";
import Utils from '../../../../helpers/utils';
import Settings from '../../../../helpers/settings';
import FilterOptionEnum from '../../../../enum/filter-option-enum';
import FilterTreeEnum from '../../../../enum/filter-tree-enum';

/**
 * The search box of a filter option
 * @extends BaseComponent
 */
class FilterSearchBox extends BaseComponent {
	constructor() {
		super();

		this.$element = null;
		this.searchValue = '';
	}

	getTemplate() {
		return `
			<div class="{{class.filterOptionShowSearchBox}}-wrapper">
				<input aria-label="Search Options" class="{{class.filterOptionShowSearchBox}}" type="text" autocomplete="on" placeholder="{{label.searchOptions}}" />
			</div>
        `;
	}

	compileTemplate() {
		return this.getTemplate()
			.replace(/{{class.filterOptionShowSearchBox}}/g, Class.filterOptionShowSearchBox)
			.replace(/{{label.searchOptions}}/g, Labels.searchOptions);
	}

	isRender() {
		if (this.parent.filterTreeType == FilterTreeEnum.FilterTreeType.HORIZONTAL) {
			return false;
		}
		
		var showSearchBoxFilterPC = this.parent.showSearchBoxFilterPC || Settings.getSettingValue('general.showSearchBoxFilterPCByDefault');
		var showSearchBoxFilterMobile = this.parent.showSearchBoxFilterMobile || Settings.getSettingValue('general.showSearchBoxFilterMobileByDefault');

		var excludeFilterTypes = [FilterOptionEnum.FilterType.PRICE, FilterOptionEnum.FilterType.PERCENT_SALE, FilterOptionEnum.FilterType.STOCK];
		var excludeDisplayTypes = [FilterOptionEnum.DisplayType.RANGE, FilterOptionEnum.DisplayType.MULTI_LEVEL_TAG, FilterOptionEnum.DisplayType.MULTI_LEVEL_COLLECTIONS];

		var isSupportSearchBox = !excludeFilterTypes.includes(this.parent.filterType)
								&& !excludeDisplayTypes.includes(this.parent.displayType);

		var isMobile = Utils.isMobile();

		return isSupportSearchBox && ((!isMobile && showSearchBoxFilterPC) || (isMobile && showSearchBoxFilterMobile));
	}

	render() {
		if (!this.$element) {
			this.$element = jQ(this.compileTemplate());
		}
	}

	isBindEvents() { return !this.isBoundEvent; }

	bindEvents() {
		if (this.$element) {
			this.$element.find('input').on('keyup', this.onKeyUp.bind(this));
			if (this.parent.$filterOptionContentElement) {
				this.parent.$filterOptionContentElement.addClass(Class.filterHasSearchBox);
			}
		}
	}

	/**
	 * Called on keyup, set the filter option items to show/hide on search.
	 * @param {Object} event - the keyup event
	 */
	onKeyUp(event) {
		this.setSearchValue(event)
		this.setFilterItemsList();
	}

	/**
	 * Set this.searchValue from the keyup event
	 * @param {Object} event - the keyup event
	 */
	setSearchValue(event){
		if (event && event.target) {
			var newSearchValue = event.target.value;
			if (typeof newSearchValue.toString == 'function') {
				newSearchValue = newSearchValue.toLowerCase().trim();
			} else {
				newSearchValue = "";
			}
			this.searchValue = newSearchValue;
		} else {
			this.searchValue = '';
		}
	}

	/**
	 * Append/remove filter item elements
	 * on the filter option
	 */
	setFilterItemsList() {

		if (!this.parent.$filterItemsContainerElement) {
			return;
		}

		if (!this.searchValue) {
			// No search value, reset the filter items to default
			var count = 0;
			var scrollFirstLoadLength = Settings.getSettingValue('general.scrollFirstLoadLength');

			this.parent.filterItems.forEach(item => {
				item.isRenderOnScroll = (this.parent.isLoadMoreOnScroll && count >= scrollFirstLoadLength);
				if (item.$element) {
					if (item.isRenderOnScroll) {
						item.$element.detach();
					} else {
						item.$element.appendTo(this.parent.$filterItemsContainerElement);
					}
				}
				count++;
			})

			// Reset the count of scrollbar
			this.parent.scrollbar.numberFilterItemsRendered = scrollFirstLoadLength;

		} else {
			// Filter the items by search value
			this.parent.filterItems.forEach(item => {
				if (item.$element) {
					if (item.label && item.label.toLowerCase().includes(this.searchValue)) {
						item.$element.appendTo(this.parent.$filterItemsContainerElement);
					} else {
						item.$element.detach();
					}
				}
			});
		}
		if (this.parent.viewMore && this.parent.viewMore.isRender()) {
			this.parent.viewMore.setVisibility();
			this.parent.viewMore.setFilterItemsVisibility();
		}
	}
}

export default FilterSearchBox;