Source: components/filter/filter-tree/filter-option-element/filter-clear-button.js

import jQ from 'jquery';

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

/**
 * Filter clear button
 */
class FilterClearButton extends BaseComponent {
	/**
	 * Creates new FilterClearButton
	 * @param {FilterTreeEnum} filterTreeType - 'vertical' or 'horizontal'
	 * @param {ClearType} clearType - clear type 'clear-single-value', 'clear-option-values', 'clear-all'
	 */
	constructor(filterTreeType, clearType) {
		super();
		this.filterTreeType = filterTreeType;
		this.clearType = clearType;
		this.requestInstantly = true;
		this.label = (this.clearType == FilterClearButton.ClearType.CLEAR_ALL) ? Labels.clearAll : Labels.clear;
	}

	/**
	 * Get the clear button types enum
	 */
	static get ClearType() {
		return {
			CLEAR_SINGLE_VALUE: 'clear-single-value',
			CLEAR_OPTION_VALUES: 'clear-option-values',
			CLEAR_ALL: 'clear-all'
		}
	}

	/**
	 * Get the clear button's html template.
	 * Depending on its clearType, it returns different templates
	 * @returns {string} Raw html template
	 */
	getTemplate() {
		switch (this.clearType) {
			case FilterClearButton.ClearType.CLEAR_SINGLE_VALUE:
				return `
					<button aria-label="{{adaLabel}}" class="{{class.button}} {{class.clearButton}}"></button>
				`;
			case FilterClearButton.ClearType.CLEAR_OPTION_VALUES:
				return `
					<button aria-label="{{adaLabel}}" class="{{class.button}} {{class.clearButton}}">{{label}}</button>
				`;
			case FilterClearButton.ClearType.CLEAR_ALL:
				return `
					<button aria-label="{{adaLabel}}" class="{{class.button}} {{class.clearAllButton}}">{{label}}</button>
				`;
			default:
				throw Error('Wrong filter clear type');
		}
	}

	compileTemplate() {
		this.buildAdaLabel();
		return this.getTemplate()
			.replace(/{{label.clear}}/g, Labels.clear)
			.replace(/{{class.button}}/g, Class.button)
			.replace(/{{class.clearButton}}/g, Class.clearButton)
			.replace(/{{class.clearAllButton}}/g, Class.clearAllButton)
			.replace(/{{adaLabel}}/g, this.adaLabel)
			.replace(/{{label}}/g, this.label);
	}

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

		if (this.isVisible()) {
			this.$element.show();
		} else {
			this.$element.hide();
		}
	}

	buildAdaLabel() {
		this.adaLabel = '';
		switch (this.clearType) {
			case FilterClearButton.ClearType.CLEAR_SINGLE_VALUE:
				var refineByItem = this.parent;
				if (refineByItem && refineByItem.type && refineByItem.label) {
					this.adaLabel = Labels.ada.clearFilterItem
						.replace(/{{filterOption}}/g, refineByItem.type)
						.replace(/{{filterItem}}/g, refineByItem.label);
				}
				break;
			case FilterClearButton.ClearType.CLEAR_OPTION_VALUES:
				var filterOption = this.parent;
				if (filterOption && filterOption.label) {
					this.adaLabel = Labels.ada.clearFilterOption.replace(/{{filterOption}}/g, filterOption.label);
				}
				break;
			case FilterClearButton.ClearType.CLEAR_ALL:
				this.adaLabel = Labels.ada.clearAllFilterItems;
				break;
		}
	}

	/**
	 * Check if the clear button is visible.
	 * For example, if there are no selected filter items,
	 * clear all button won't be visible.
	 * @returns {boolean}
	 */
	isVisible() {
		var isShowClearButton = true;
		switch (this.clearType) {
			case FilterClearButton.ClearType.CLEAR_OPTION_VALUES:
				var filterOption = this.parent;
				if (filterOption.displayType == FilterOptionEnum.DisplayType.MULTI_LEVEL_COLLECTIONS) {
					var filterOptionTagId = filterOption.filterOptionId.replace(Globals.prefix + '_c_', Globals.prefix + '_ct_');
					isShowClearButton = Globals.queryParams[filterOption.filterOptionId] || Globals.queryParams[filterOptionTagId];
				} else {
					isShowClearButton = Globals.queryParams[filterOption.filterOptionId];
				}
				break;
			case FilterClearButton.ClearType.CLEAR_ALL:
				isShowClearButton = false;
				Object.keys(Globals.queryParams).forEach(key => {
					// Check if there is any filter option param
					if (key.startsWith(Globals.prefix)) {
						var value = Globals.queryParams[key];
						var isCollectionAllInSearchPage = Utils.isSearchPage() && key.startsWith(Globals.prefix + '_c_') && value == 0;

						// Show clear button when there is filter option param
						// EXCEPT when filter by collection All in search page
						if (!isCollectionAllInSearchPage) {
							isShowClearButton = true;
						}
					}
				})

				break;

		}
		return !!(isShowClearButton);
	}

	isBindEvents() { return !this.isBoundEvent; }

	bindEvents() {
		if (this.$element) {
			this.$element.on('click', this.onClick.bind(this));
		}
	}

	/**
	 * On click the clear button
	 * calls onClearSingleValue, or onClearOptionValues, or onClearAll depending on clear type
	 * @param event
	 */
	onClick(event) {
		if (event) {
			event.preventDefault();
		}
		if (this.requestInstantly) {
			switch(this.clearType) {
				case FilterClearButton.ClearType.CLEAR_SINGLE_VALUE:
					this.onClearSingleValue();
					break;
				case FilterClearButton.ClearType.CLEAR_OPTION_VALUES:
					this.onClearOptionValues();
					break;
				case FilterClearButton.ClearType.CLEAR_ALL:
					this.onClearAll();
					break;
			}
		} else {
			switch(this.clearType) {
				case FilterClearButton.ClearType.CLEAR_OPTION_VALUES:
					this.onDeselectOptionValues();
					break;
			}
		}
	}

	/**
	 * Clears single filter value
	 * This clear button is a child of the RefineByItem component,
	 * RefineByItem component has filterOptionId and filterItemId fields, to know which value to clear.
	 * Calls API
	 */
	onClearSingleValue() {
		var refineByItem = this.parent;
		var filterOptionId = refineByItem.filterOptionId;
		var filterOptionValue = refineByItem.filterItemId;

		var currentValues = Globals.queryParams[filterOptionId];
		if (currentValues == null) return;
		if (!Array.isArray(currentValues)) {
			currentValues = [currentValues];
		}

		var removeAtIndex = currentValues.indexOf(filterOptionValue);
		var newValues = [];
		if (removeAtIndex > -1) {
			newValues = currentValues.filter((value, index) => index != removeAtIndex);
		}
		// Clear values
		if (newValues.length == 0) {
			FilterApi.setParam(filterOptionId, null);
			FilterApi.setParam(filterOptionId + '_and_condition', null);
			FilterApi.setParam(filterOptionId + '_show_exact_rating', null);
			FilterApi.setParam(filterOptionId + '_exclude_from_value', null);
		} else {
			FilterApi.setParam(filterOptionId, newValues);
		}
		// On search page, if clear multi-level tag, also clear the collection
		if (Utils.isSearchPage() && filterOptionId.startsWith(Globals.prefix + '_ct_')) {
			var collectionFilterOptionId = filterOptionId.replace(Globals.prefix + '_ct_', Globals.prefix + '_c_');
			FilterApi.setParam(collectionFilterOptionId, null);
		}
		// Reset page
		FilterApi.setParam('page', 1);
		var eventType = 'clear';
		var eventInfo = {
			filterOptionId: filterOptionId,
			filterOptionValue: filterOptionValue
		}
		FilterApi.applyFilter(eventType, eventInfo);
	}

	/**
	 * Clear all values of one filter options
	 * This button is a child of a FilterOption component
	 * FilterOption component has filterOptionId field, to know which values to clear.
	 * Calls API.
	 */
	onClearOptionValues() {
		// Close the tab on horizontal
		var filterOption = this.parent;
		if (filterOption.filterTreeType == FilterTreeEnum.FilterTreeType.HORIZONTAL && !Settings.getSettingValue('general.keepTabOpenState') && filterOption.collapse) {
			this.parent.collapse.onToggleHorizontal();
		}

		Globals.internalClick = true;

		var filterOptionId = filterOption.filterOptionId;
		// With multi collection on collection page, don't clear the collection, only clear the tags
		if (filterOption.displayType == FilterOptionEnum.DisplayType.MULTI_LEVEL_COLLECTIONS && !Utils.isSearchPage()) {
			filterOptionId = filterOptionId.replace(Globals.prefix + '_c_', Globals.prefix + '_ct_');
		}

		var eventType = 'clear';
		var eventInfo = {
			filterOptionId: filterOptionId
		}

		FilterApi.setParam('page', 1);
		FilterApi.setParam(filterOptionId, null);
		FilterApi.setParam(filterOptionId + '_and_condition', null);
		FilterApi.setParam(filterOptionId + '_show_exact_rating', null);
		FilterApi.setParam(filterOptionId + '_exclude_from_value', null);
		FilterApi.applyFilter(eventType, eventInfo);
	}

	/**
	 * Clear all values of the filter
	 * This button is a child of a FilterTree component
	 * Calls API.
	 */
	onClearAll() {
		var currentFilterOptionIds = [];
		Object.keys(Globals.queryParams).forEach(queryParam => {
			if (queryParam.startsWith(Globals.prefix)) {
				currentFilterOptionIds.push(queryParam);
			}
		});

		currentFilterOptionIds.forEach(filterOptionId => {
			FilterApi.setParam(filterOptionId, null);
		});
		FilterApi.setParam('page', 1);
		
		var eventType = 'clearAll';
		var eventInfo = {};
		FilterApi.applyFilter(eventType, eventInfo);
	}

	/**
	 * Deselects all filter option item in a filter option
	 * Don't call API.
	 */
	onDeselectOptionValues() {
		if (this.parent.filterItems) {
			this.parent.filterItems.forEach(filterItem => {
				filterItem.$element.removeClass('selected');
				filterItem.isSelected = false;
			})
		}
	}
}

export default FilterClearButton;