Source: components/filter/filter-tree/filter-option-element/filter-scrollbar.js

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

/**
 * The scrollbar of a filter option.
 * @extends BaseComponent
 */
class FilterScrollbar extends BaseComponent {
	constructor() {
		super();
		this.placeHolderHeight = '';
		this.numberFilterItemsRendered = 0;
		this.$scrollElement = null;
	}

	/**
	 * Get the scroll state of a filter option.
	 * This is a static field, so it won't be destroyed along with class instance.
	 * @param {string} filterOptionId
	 * @return {number} - scrollTop value.
	 */
	static getScrollStateData(filterOptionId) {
		return scrollStateData.get(filterOptionId);
	}

	/**
	 * Store the scroll state of a filter option.
	 * This is a static field, so it won't be destroyed along with class instance.
	 * @param {string} filterOptionId
	 * @param {number} scrollTop - scrollTop value
	 */
	static setScrollStateData(filterOptionId, scrollTop) {
		scrollStateData.set(filterOptionId, scrollTop);
	}
	
	isBindEvents() { return !this.isBoundEvent; }

	bindEvents() {
		if (this.parent.$element && FilterScrollbar.isEnabled(this.parent.displayType, this.parent.filterType, this.parent.showMoreType)) {

			// Get scroll container
			var $scrollbarContainer = this.parent.$element.find('.' + Class.filterOptionContentInner);
			if ($scrollbarContainer.length > 0) {

				// Assign scrollbar
				this.$scrollElement = $scrollbarContainer;

				// Bind loadmore on scroll
				if (this.parent.isLoadMoreOnScroll || Settings.getSettingValue('general.keepScrollState')) {
					this.$scrollElement.on('scroll', this.onScroll.bind(this));
				}

				// Keep scroll state
				if (Settings.getSettingValue('general.keepScrollState')) {
					var scrollTop = FilterScrollbar.getScrollStateData(this.parent.filterOptionId);
					if (!isNaN(scrollTop)) {
						this.$scrollElement[0].scrollTop = scrollTop;
					}
				}
			}
		}
	}

	/**
	 * Check if scrollbar is enabled.
	 * @param {FilterOptionEnum.DisplayType} displayType
	 * @param {FilterOptionEnum.ShowMoreType} showMoreType
	 * @return {boolean}
	 */
	static isEnabled(displayType, filterType, showMoreType) {
		var excludeDisplayTypes = [FilterOptionEnum.DisplayType.RANGE];
		var excludeFilterTypes = [
			FilterOptionEnum.FilterType.REVIEW_RATINGS,
			FilterOptionEnum.FilterType.STOCK,
			FilterOptionEnum.FilterType.PERCENT_SALE,
			FilterOptionEnum.FilterType.PRICE,
			FilterOptionEnum.FilterType.VARIANTS_PRICE
		];
		var hasScrollbar = showMoreType == FilterOptionEnum.ShowMoreType.SCROLLBAR || showMoreType == FilterOptionEnum.ShowMoreType.VIEWMORE_SCROLLBAR;
		var isMobile = Utils.isMobile();
		var activeScrollbarByDevice = (isMobile && Settings.getSettingValue('general.activeFilterScrollbarMobile')) || (!isMobile && Settings.getSettingValue('general.activeFilterScrollbarPC'));
		return !excludeDisplayTypes.includes(displayType)
			&& !excludeFilterTypes.includes(filterType)
			&& hasScrollbar
			&& activeScrollbarByDevice;
	}

	/**
	 * On scroll filter option
	 */
	onScroll() {
		if (Settings.getSettingValue('general.keepScrollState')) {
			var scrollTop = this.$scrollElement[0].scrollTop;
			FilterScrollbar.setScrollStateData(this.parent.filterOptionId, scrollTop);
		}

		if (this.parent.isLoadMoreOnScroll && this.isScrollToBottom()) {
			var isSearching = this.parent.searchBox && this.parent.searchBox.searchValue;
			if (!isSearching) {
				this.appendFilterItems();
			}
		}
	}

	/**
	 * Check if scroll to bottom of a filter option
	 * @return {boolean}
	 */
	isScrollToBottom() {
		if (this.$scrollElement) {
			return this.$scrollElement[0].scrollHeight - this.$scrollElement.scrollTop() - this.$scrollElement.outerHeight() < 1 ;
		}
		return false;
	}

	/**
	 * Appends filter items on scroll to bottom of a filter option
	 */
	appendFilterItems() {
		if (this.parent.$filterItemsContainerElement) {
			// Check if all items are rendered
			var numberItems = Settings.getSettingValue('general.scrollFirstLoadLength');

			if (this.numberFilterItemsRendered == 0) {
				this.numberFilterItemsRendered = numberItems;
			} else if (this.numberFilterItemsRendered == this.parent.filterItems.size) {
				return;
			}

			// Css class to display spinner
			this.parent.$element.addClass('boost-pfs-filter-scrollbar-loading');

			// Append filter items
			var count = 0;
			this.parent.filterItems.forEach((filterItem) => {
				if (filterItem.isRenderOnScroll && count < numberItems) {
					this.parent.$filterItemsContainerElement.append(filterItem.$element);
					filterItem.isRenderOnScroll = false;
					count++;
					this.numberFilterItemsRendered++;
				}
			})

			// Remove spinner with a timeout because spinner get removed too fast
			setTimeout(function () {
				this.parent.$element.removeClass('boost-pfs-filter-scrollbar-loading');
			}.bind(this), 200);
		}
	}
}

var scrollStateData = new Map();

export default FilterScrollbar;