SHX-Theme/assets/facets.js

356 lines
13 KiB
JavaScript

class FacetFiltersForm extends HTMLElement {
constructor() {
super();
this.onActiveFilterClick = this.onActiveFilterClick.bind(this);
this.debouncedOnSubmit = debounce((event) => {
this.onSubmitHandler(event);
}, 500);
const facetForm = this.querySelector('form');
facetForm.addEventListener('input', this.debouncedOnSubmit.bind(this));
const facetWrapper = this.querySelector('#FacetsWrapperDesktop');
if (facetWrapper) facetWrapper.addEventListener('keyup', onKeyUpEscape);
}
static setListeners() {
const onHistoryChange = (event) => {
const searchParams = event.state ? event.state.searchParams : FacetFiltersForm.searchParamsInitial;
if (searchParams === FacetFiltersForm.searchParamsPrev) return;
FacetFiltersForm.renderPage(searchParams, null, false);
};
window.addEventListener('popstate', onHistoryChange);
}
static toggleActiveFacets(disable = true) {
document.querySelectorAll('.js-facet-remove').forEach((element) => {
element.classList.toggle('disabled', disable);
});
}
static renderPage(searchParams, event, updateURLHash = true) {
FacetFiltersForm.searchParamsPrev = searchParams;
const sections = FacetFiltersForm.getSections();
const countContainer = document.getElementById('ProductCount');
const countContainerDesktop = document.getElementById('ProductCountDesktop');
const loadingSpinners = document.querySelectorAll(
'.facets-container .loading__spinner, facet-filters-form .loading__spinner'
);
loadingSpinners.forEach((spinner) => spinner.classList.remove('hidden'));
document.getElementById('ProductGridContainer').querySelector('.collection').classList.add('loading');
if (countContainer) {
countContainer.classList.add('loading');
}
if (countContainerDesktop) {
countContainerDesktop.classList.add('loading');
}
sections.forEach((section) => {
const url = `${window.location.pathname}?section_id=${section.section}&${searchParams}`;
const filterDataUrl = (element) => element.url === url;
FacetFiltersForm.filterData.some(filterDataUrl)
? FacetFiltersForm.renderSectionFromCache(filterDataUrl, event)
: FacetFiltersForm.renderSectionFromFetch(url, event);
});
if (updateURLHash) FacetFiltersForm.updateURLHash(searchParams);
}
static renderSectionFromFetch(url, event) {
fetch(url)
.then((response) => response.text())
.then((responseText) => {
const html = responseText;
FacetFiltersForm.filterData = [...FacetFiltersForm.filterData, { html, url }];
FacetFiltersForm.renderFilters(html, event);
FacetFiltersForm.renderProductGridContainer(html);
FacetFiltersForm.renderProductCount(html);
if (typeof initializeScrollAnimationTrigger === 'function') initializeScrollAnimationTrigger(html.innerHTML);
});
}
static renderSectionFromCache(filterDataUrl, event) {
const html = FacetFiltersForm.filterData.find(filterDataUrl).html;
FacetFiltersForm.renderFilters(html, event);
FacetFiltersForm.renderProductGridContainer(html);
FacetFiltersForm.renderProductCount(html);
if (typeof initializeScrollAnimationTrigger === 'function') initializeScrollAnimationTrigger(html.innerHTML);
}
static renderProductGridContainer(html) {
document.getElementById('ProductGridContainer').innerHTML = new DOMParser()
.parseFromString(html, 'text/html')
.getElementById('ProductGridContainer').innerHTML;
document
.getElementById('ProductGridContainer')
.querySelectorAll('.scroll-trigger')
.forEach((element) => {
element.classList.add('scroll-trigger--cancel');
});
}
static renderProductCount(html) {
const count = new DOMParser().parseFromString(html, 'text/html').getElementById('ProductCount').innerHTML;
const container = document.getElementById('ProductCount');
const containerDesktop = document.getElementById('ProductCountDesktop');
container.innerHTML = count;
container.classList.remove('loading');
if (containerDesktop) {
containerDesktop.innerHTML = count;
containerDesktop.classList.remove('loading');
}
const loadingSpinners = document.querySelectorAll(
'.facets-container .loading__spinner, facet-filters-form .loading__spinner'
);
loadingSpinners.forEach((spinner) => spinner.classList.add('hidden'));
}
static renderFilters(html, event) {
const parsedHTML = new DOMParser().parseFromString(html, 'text/html');
const facetDetailsElementsFromFetch = parsedHTML.querySelectorAll(
'#FacetFiltersForm .js-filter, #FacetFiltersFormMobile .js-filter, #FacetFiltersPillsForm .js-filter'
);
const facetDetailsElementsFromDom = document.querySelectorAll(
'#FacetFiltersForm .js-filter, #FacetFiltersFormMobile .js-filter, #FacetFiltersPillsForm .js-filter'
);
// Remove facets that are no longer returned from the server
Array.from(facetDetailsElementsFromDom).forEach((currentElement) => {
if (!Array.from(facetDetailsElementsFromFetch).some(({ id }) => currentElement.id === id)) {
currentElement.remove();
}
});
const matchesId = (element) => {
const jsFilter = event ? event.target.closest('.js-filter') : undefined;
return jsFilter ? element.id === jsFilter.id : false;
};
const facetsToRender = Array.from(facetDetailsElementsFromFetch).filter((element) => !matchesId(element));
const countsToRender = Array.from(facetDetailsElementsFromFetch).find(matchesId);
facetsToRender.forEach((elementToRender, index) => {
const currentElement = document.getElementById(elementToRender.id);
// Element already rendered in the DOM so just update the innerHTML
if (currentElement) {
document.getElementById(elementToRender.id).innerHTML = elementToRender.innerHTML;
} else {
if (index > 0) {
const { className: previousElementClassName, id: previousElementId } = facetsToRender[index - 1];
// Same facet type (eg horizontal/vertical or drawer/mobile)
if (elementToRender.className === previousElementClassName) {
document.getElementById(previousElementId).after(elementToRender);
return;
}
}
if (elementToRender.parentElement) {
document.querySelector(`#${elementToRender.parentElement.id} .js-filter`).before(elementToRender);
}
}
});
FacetFiltersForm.renderActiveFacets(parsedHTML);
FacetFiltersForm.renderAdditionalElements(parsedHTML);
if (countsToRender) {
const closestJSFilterID = event.target.closest('.js-filter').id;
if (closestJSFilterID) {
FacetFiltersForm.renderCounts(countsToRender, event.target.closest('.js-filter'));
FacetFiltersForm.renderMobileCounts(countsToRender, document.getElementById(closestJSFilterID));
const newFacetDetailsElement = document.getElementById(closestJSFilterID);
const newElementSelector = newFacetDetailsElement.classList.contains('mobile-facets__details')
? `.mobile-facets__close-button`
: `.facets__summary`;
const newElementToActivate = newFacetDetailsElement.querySelector(newElementSelector);
if (newElementToActivate) newElementToActivate.focus();
}
}
}
static renderActiveFacets(html) {
const activeFacetElementSelectors = ['.active-facets-mobile', '.active-facets-desktop'];
activeFacetElementSelectors.forEach((selector) => {
const activeFacetsElement = html.querySelector(selector);
if (!activeFacetsElement) return;
document.querySelector(selector).innerHTML = activeFacetsElement.innerHTML;
});
FacetFiltersForm.toggleActiveFacets(false);
}
static renderAdditionalElements(html) {
const mobileElementSelectors = ['.mobile-facets__open', '.mobile-facets__count', '.sorting'];
mobileElementSelectors.forEach((selector) => {
if (!html.querySelector(selector)) return;
document.querySelector(selector).innerHTML = html.querySelector(selector).innerHTML;
});
document.getElementById('FacetFiltersFormMobile').closest('menu-drawer').bindEvents();
}
static renderCounts(source, target) {
const targetSummary = target.querySelector('.facets__summary');
const sourceSummary = source.querySelector('.facets__summary');
if (sourceSummary && targetSummary) {
targetSummary.outerHTML = sourceSummary.outerHTML;
}
const targetHeaderElement = target.querySelector('.facets__header');
const sourceHeaderElement = source.querySelector('.facets__header');
if (sourceHeaderElement && targetHeaderElement) {
targetHeaderElement.outerHTML = sourceHeaderElement.outerHTML;
}
const targetWrapElement = target.querySelector('.facets-wrap');
const sourceWrapElement = source.querySelector('.facets-wrap');
if (sourceWrapElement && targetWrapElement) {
const isShowingMore = Boolean(target.querySelector('show-more-button .label-show-more.hidden'));
if (isShowingMore) {
sourceWrapElement
.querySelectorAll('.facets__item.hidden')
.forEach((hiddenItem) => hiddenItem.classList.replace('hidden', 'show-more-item'));
}
targetWrapElement.outerHTML = sourceWrapElement.outerHTML;
}
}
static renderMobileCounts(source, target) {
const targetFacetsList = target.querySelector('.mobile-facets__list');
const sourceFacetsList = source.querySelector('.mobile-facets__list');
if (sourceFacetsList && targetFacetsList) {
targetFacetsList.outerHTML = sourceFacetsList.outerHTML;
}
}
static updateURLHash(searchParams) {
history.pushState({ searchParams }, '', `${window.location.pathname}${searchParams && '?'.concat(searchParams)}`);
}
static getSections() {
return [
{
section: document.getElementById('product-grid').dataset.id,
},
];
}
createSearchParams(form) {
const formData = new FormData(form);
return new URLSearchParams(formData).toString();
}
onSubmitForm(searchParams, event) {
FacetFiltersForm.renderPage(searchParams, event);
}
onSubmitHandler(event) {
event.preventDefault();
const sortFilterForms = document.querySelectorAll('facet-filters-form form');
if (event.srcElement.className == 'mobile-facets__checkbox') {
const searchParams = this.createSearchParams(event.target.closest('form'));
this.onSubmitForm(searchParams, event);
} else {
const forms = [];
const isMobile = event.target.closest('form').id === 'FacetFiltersFormMobile';
sortFilterForms.forEach((form) => {
if (!isMobile) {
if (form.id === 'FacetSortForm' || form.id === 'FacetFiltersForm' || form.id === 'FacetSortDrawerForm') {
const noJsElements = document.querySelectorAll('.no-js-list');
noJsElements.forEach((el) => el.remove());
forms.push(this.createSearchParams(form));
}
} else if (form.id === 'FacetFiltersFormMobile') {
forms.push(this.createSearchParams(form));
}
});
this.onSubmitForm(forms.join('&'), event);
}
}
onActiveFilterClick(event) {
event.preventDefault();
FacetFiltersForm.toggleActiveFacets();
const url =
event.currentTarget.href.indexOf('?') == -1
? ''
: event.currentTarget.href.slice(event.currentTarget.href.indexOf('?') + 1);
FacetFiltersForm.renderPage(url);
}
}
FacetFiltersForm.filterData = [];
FacetFiltersForm.searchParamsInitial = window.location.search.slice(1);
FacetFiltersForm.searchParamsPrev = window.location.search.slice(1);
customElements.define('facet-filters-form', FacetFiltersForm);
FacetFiltersForm.setListeners();
class PriceRange extends HTMLElement {
constructor() {
super();
this.querySelectorAll('input').forEach((element) =>
element.addEventListener('change', this.onRangeChange.bind(this))
);
this.setMinAndMaxValues();
}
onRangeChange(event) {
this.adjustToValidValues(event.currentTarget);
this.setMinAndMaxValues();
}
setMinAndMaxValues() {
const inputs = this.querySelectorAll('input');
const minInput = inputs[0];
const maxInput = inputs[1];
if (maxInput.value) minInput.setAttribute('max', maxInput.value);
if (minInput.value) maxInput.setAttribute('min', minInput.value);
if (minInput.value === '') maxInput.setAttribute('min', 0);
if (maxInput.value === '') minInput.setAttribute('max', maxInput.getAttribute('max'));
}
adjustToValidValues(input) {
const value = Number(input.value);
const min = Number(input.getAttribute('min'));
const max = Number(input.getAttribute('max'));
if (value < min) input.value = min;
if (value > max) input.value = max;
}
}
customElements.define('price-range', PriceRange);
class FacetRemove extends HTMLElement {
constructor() {
super();
const facetLink = this.querySelector('a');
facetLink.setAttribute('role', 'button');
facetLink.addEventListener('click', this.closeFilter.bind(this));
facetLink.addEventListener('keyup', (event) => {
event.preventDefault();
if (event.code.toUpperCase() === 'SPACE') this.closeFilter(event);
});
}
closeFilter(event) {
event.preventDefault();
const form = this.closest('facet-filters-form') || document.querySelector('facet-filters-form');
form.onActiveFilterClick(event);
}
}
customElements.define('facet-remove', FacetRemove);