Source: components/filter/filter-tree/filter-refine-by/filter-refine-by-item.js

import jQ from 'jquery';

import BaseComponent from "../../../base-component";
import FilterTreeEnum from "../../../../enum/filter-tree-enum";
import Class from "../../../../helpers/class";
import Labels from "../../../../helpers/labels";
import FilterClearButton from "../filter-option-element/filter-clear-button";
import FilterOptionEnum from '../../../../enum/filter-option-enum';
import FilterOptionItem from "../filter-option-item/filter-option-item";

/**
 * A single refine by item
 * It has a label and a clear button.
 */
class FilterRefineByItem extends BaseComponent {
	/**
	 * Creates a new FilterRefineByItem
	 * @param {FilterTreeEnum} filterTreeType - 'vertical' or 'horizontal'
	 */
	constructor(filterTreeType) {
		super();
		this.filterTreeType = filterTreeType;
		this.$element = null;
	}

	init() {
		this.clearButton = new FilterClearButton(this.filterTreeType, FilterClearButton.ClearType.CLEAR_SINGLE_VALUE);
		this.addComponent(this.clearButton);
	}

	/**
	 * Get the refine by item's html template.
	 * Depending on its filterTreeType, it returns different templates for 'vertical' and 'horizontal'
	 * @returns {string} Raw html template
	 */
	getTemplate() {
		switch (this.filterTreeType){
			case FilterTreeEnum.FilterTreeType.VERTICAL:
				return `
					<div class="refine-by-item {{class.filterOptionLabel}}">
						<a tabindex="-1" href="javascript:;">
							<span class="refine-by-type">
								<span>{{type}}</span><strong>{{label}}</strong>
							</span>
							{{clearButton}}
						</a>
					</div>
				`;
			case FilterTreeEnum.FilterTreeType.HORIZONTAL:
				return `
					<div class="refine-by-item">
						<a tabindex="-1" href="javascript:;">
							<span class="{{class.filterOptionLabel}} refine-by-type">{{label}}</span>
							{{clearButton}}
						</a>
					</div>
				`;
				
			default: 
				throw Error('Wrong filterTreeType');	
		}
	}

	compileTemplate() {
		return this.getTemplate()
			.replace(/{{class.filterOptionLabel}}/g, Class.filterOptionLabel)
			.replace(/{{type}}/g, this.type ? this.type + ': ' : '')
			.replace(/{{label}}/g, this.label)
			.replace(/{{clearButton}}/g, '');
	}

	render() {
		this.$element = jQ(this.compileTemplate());
		this.$element.find('a').append(this.clearButton.$element);
	}

	/**
	 * Set data for refine by item.
	 * @param {FilterOption} filterOption - currently selected filter option
	 * @param {FilterOptionItem} filterItem - currently selected filter option item
	 * @param {string} filterItemKey - the key of the above filter option item
	 */
	setData(filterOption, filterItem, filterItemKey) {
		this.filterOption = filterOption;
		this.filterItem = filterItem;
		
		this.filterOptionId = filterOption.filterOptionId;
		this.filterItemId = filterItemKey;
		this.type = filterOption.label;

		// Build label
		this.label = this.buildLabel();
	}

	/**
	 * Build label for refine by item.
	 * It gets the label from filterItem.label, except for some cases (Rating, Range)
	 * @returns {string}
	 */
	buildLabel() {
		var formatLabel = this.filterItemId;

		if (this.filterOption) {
			switch (this.filterOption.displayType) {
				case FilterOptionEnum.DisplayType.RANGE:
					formatLabel = this.buildLabelRange();
					break;
				case FilterOptionEnum.DisplayType.RATING:
					formatLabel = this.buildLabelRating();
					break;
				case FilterOptionEnum.DisplayType.MULTI_LEVEL_COLLECTIONS:
				case FilterOptionEnum.DisplayType.MULTI_LEVEL_TAG:
					formatLabel = this.buildLabelMultiLevel();
					break;
				default:
					if (this.filterItem) {
						formatLabel = this.filterItem.label;
					} else {
						// Fix issue when user filter by a value but the value doesn't exist in filter tree
						var dummyFilterItem = new FilterOptionItem(this.filterOption.filterTreeType);
						dummyFilterItem.filterOption = this.filterOption;
						dummyFilterItem.label = this.filterItemId;
						formatLabel = dummyFilterItem.buildLabel();
					}
					break;
			}
		}

		return formatLabel;
	}

	/**
	 * Build special refine by label for filter by rating
	 * @returns {string} - Formatted label
	 */
	buildLabelRating() {
		var label = '';
		if (this.filterItem) { 
			var from = this.filterItem.from;
			label = Math.ceil(from) + ' ';
			if (Math.ceil(from) == 1) {
				label += Labels.ratingStar; 
			} else {
				label += Labels.ratingStars;
			}
			if (!this.filterOption.showExactRating) {
				label += ' ' + Labels.ratingUp;
			}
		}
		return label;
	}

	/**
	 * Build special refine by label for filter by range
	 * @returns {string} - Formatted label
	 */
	buildLabelRange() {
		var label = '';
		if (this.filterOption) {
			if (this.filterOption.isNumberRangeSlider) {
				var values = this.filterItemId.split(':');
				if (values && values.length == 2) {
					var minLabel = this.filterOption.buildLabel(values[0]);
					var maxLabel = this.filterOption.buildLabel(values[1]);
					if (minLabel == maxLabel) {
						label = minLabel;
					} else {
						label = minLabel + ' - ' + maxLabel;
					}
				}
			} else {
				var values = this.filterItemId;
				var minLabel = '';
				var maxLabel = '';
				// Get label from filter option data
				if ((this.filterOption.currentMax - 1 > 0)
					&& (this.filterOption.currentMax - 1 < this.filterOption.valuesData.length)) {
					minLabel = this.filterOption.valuesData[this.filterOption.currentMin].key;
					maxLabel = this.filterOption.valuesData[this.filterOption.currentMax - 1].key;

				// Fallback: get label from filter item id
				} else if (Array.isArray(values) && values.length > 0){
					minLabel = values[0];
					maxLabel = values[values.length - 1];
				}

				// Remove prefix
				if (this.filterOption.prefix) {
					var prefix = this.filterOption.prefix.replace(/\\/g, '');
					minLabel = minLabel.replace(prefix, '').trim();
					maxLabel = maxLabel.replace(prefix, '').trim();
				}

				// Build label min-max
				if (minLabel == maxLabel) {
					label = minLabel;
				} else {
					label = minLabel + ' - ' + maxLabel;
				}
			}
		}
		return label;
	}

	/**
	 * Build refine by label for filter by multi level tag
	 * @returns {string} - Formatted label
	 */
	buildLabelMultiLevel() {
		var label = '';
		// Different logic for multi level
		if (this.filterOption && this.filterItem &&
				(this.filterOption.displayType == FilterOptionEnum.DisplayType.MULTI_LEVEL_COLLECTIONS
				|| this.filterOption.displayType == FilterOptionEnum.DisplayType.MULTI_LEVEL_TAG)) {

			switch (this.filterItem.level) {
				case 1:
					this.type = this.filterOption.label;
					label = this.filterItem.buildLabel();
					break;
				case 2:
					this.filterOptionId = this.filterOptionId.replace(Globals.prefix + '_c_', Globals.prefix + '_ct_');
					this.type = this.filterOption.label;
					label = this.filterItem.buildLabel();
					break;
				case 3:
					this.filterOptionId = this.filterOptionId.replace(Globals.prefix + '_c_', Globals.prefix + '_ct_');
					this.type = this.filterOption.label;
					label = this.filterItem.buildLabel();
					break;
			}
		}
		return label ? label : this.filterItemId;
	}
}
export default FilterRefineByItem;