Source: components/filter/filter-tree/filter-option-element/filter-view-more.js

import jQ from 'jquery';

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

/**
 * The view more button of a filter option
 * @extends BaseComponent
 */
class FilterViewMore extends BaseComponent {
	/**
	 * Creates a new FilterViewMore button
	 * @param {FilterTreeEnum} filterTreeType - 'vertical' or 'horizontal'
	 */
	constructor(filterTreeType) {
		super();

		this.filterTreeType = filterTreeType;
		this.isExpanded = false;
		this.label = Labels.viewMore;
		this.class = Class.filterOptionViewMore;
		this.isVisible = true;
		this.$element = null;
	}

	/**
	 * Get the view more state of a filter option.
	 * This is a static field, so it won't be destroyed along with class instance.
	 * @param {string} filterOptionId
	 * @return {boolean} - isExpanded value.
	 */
	static getViewMoreStateData(filterOptionId) {
		return viewMoreStateData.get(filterOptionId);
	}

	/**
	 * Store the view more state of a filter option.
	 * This is a static field, so it won't be destroyed along with class instance.
	 * @param {string} filterOptionId
	 * @param {boolean} isExpanded - is the view more expanded or not
	 */
	static setViewMoreStateData(filterOptionId, isExpanded) {
		viewMoreStateData.set(filterOptionId, isExpanded);
	}

	init() {
		this.numberVisibleItems = this.getNumberVisibleItems();
	}

	/**
	 * Get html template of view more button
	 * 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="{{class.button}} {{class}}"><button>{{label}}</button></div>
				`;
			case FilterTreeEnum.FilterTreeType.HORIZONTAL:
				return `
					<div class="{{class.button}} {{class}}"><button aria-label="{{label}}"></button></div>
				`;
			default:
				throw Error('Pass a filter tree type into the constructor');
		}
	}

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

	isRender() {
		return this.parent.displayType != FilterOptionEnum.DisplayType.RANGE 
				&& (this.parent.showMoreType == FilterOptionEnum.ShowMoreType.VIEWMORE 
				|| this.parent.showMoreType == FilterOptionEnum.ShowMoreType.VIEWMORE_SCROLLBAR);
	}

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

	bindEvents() {
		if (this.$element) {
			if (!this.isBoundEvent) {
				this.$element.on('click', this.onClick.bind(this));
			}
			// Add class to parent for CSS
			if (this.parent.$filterOptionContentElement) {
				this.parent.$filterOptionContentElement.addClass(Class.filterHasViewMore);
			}
			// Expand the view more if it was expanded before
			if (!this.isExpanded && FilterViewMore.getViewMoreStateData(this.parent.filterOptionId)) {
				this.onClick();
			// Else just calculate how many items are visible
			} else {
				this.setFilterItemsVisibility();
			}
		}
	}

	/**
	 * On clicking the filter view more
	 * Toggle show more/show less items
	 */
	onClick() {
		this.isExpanded = !this.isExpanded;
		FilterViewMore.setViewMoreStateData(this.parent.filterOptionId, this.isExpanded);

		var oldClass = '';
		if (this.isExpanded) {
			this.label = Labels.viewLess;
			this.class = Class.filterOptionViewLess;
			oldClass = Class.filterOptionViewMore;
		} else {
			this.label = Labels.viewMore;
			this.class = Class.filterOptionViewMore;
			oldClass = Class.filterOptionViewLess;
		}
		if (this.filterTreeType == FilterTreeEnum.FilterTreeType.VERTICAL) {
			this.$element.find('button').html(this.label);
		} else {
			this.$element.removeClass(oldClass).addClass(this.class);
		}
		this.setFilterItemsVisibility();
	}

	/**
	 * Check settings for how many items are visible before we show 'view more'
	 */
	getNumberVisibleItems() {
		var numberVisibleItems = Settings.getSettingValue('general.startViewMore')[this.parent.displayType];

		if (this.filterTreeType == FilterTreeEnum.FilterTreeType.HORIZONTAL) {
			var filterHorizontalColumn = Settings.getSettingValue('general.filterHorizontalColumn')
			if (Number.isInteger(filterHorizontalColumn)) {
				numberVisibleItems = numberVisibleItems * filterHorizontalColumn;
			} else {
				numberVisibleItems = Settings.getSettingValue('general.startViewMoreH')[this.parent.displayType];
			}
		}

		if (!numberVisibleItems || numberVisibleItems <= 1) {
			numberVisibleItems = 5;
		}
		return numberVisibleItems;
	}

	/**
	 * Set visibility of the view more button
	 * If there are too few items (<=5), view more button will be hidden
	 */
	setVisibility() {
		if (this.$element) {
			// Get number of filter items on first load (parent not yet rendered)
			var numberFilterItems = this.parent.filterItems.size;

			if (!this.parent.keepValuesStatic && !Settings.getSettingValue('general.showOutOfStockOption')) {
				var filterItemsArray = Array.from(this.parent.filterItems, ([key, value]) => ({ key, value }));
				var filterItemsAvailble = filterItemsArray.filter((fitlerItem) => {
					return fitlerItem.value.docCount > 0;
				});
				numberFilterItems = filterItemsAvailble.length;
			}
			
			// Get number filter items on the DOM (after search box or load more)
			if (this.parent.$filterItemsContainerElement) {
				numberFilterItems  = this.parent.$filterItemsContainerElement.find('li').length;
			}

			this.isVisible = numberFilterItems > this.numberVisibleItems;
			if (this.isVisible) {
				this.$element.show();
			} else {
				this.$element.hide();
			}
		} else {
			this.isVisible = false;
		}
	}

	/**
	 * Show/hide the filter option items on clicking view more/view less
	 */
	setFilterItemsVisibility() {
		if (this.parent.$filterItemsContainerElement) {
			var $filterItemElements = this.parent.$filterItemsContainerElement.find('li');

			$filterItemElements.show();

			if (!this.isExpanded) {
				var sliceFrom = this.numberVisibleItems;
				var sliceTo = $filterItemElements.length;
				$filterItemElements.slice(sliceFrom, sliceTo).hide();
			}
		}
	}
}

var viewMoreStateData = new Map();

export default FilterViewMore;