@arrow-ecs/reflex
Version:
Reflex UI library
455 lines (376 loc) • 14.5 kB
JavaScript
const disableFilter = (toggle) => {
if (toggle.classList.contains('show')) {
toggle.classList.remove('show');
setTimeout(() => {
toggle.classList.remove('showing');
}, 300);
return true;
}
return false;
}
const toggleAllOptions = (enableAll, menu) => {
const menuOptions = menu.querySelectorAll('.select-menu-option input');
for (const option of menuOptions) {
option.checked = enableAll;
}
}
const toggleFilterHandler = (toggle) => {
// remove active styling on toggle
if (toggle.classList.contains('active')) {
toggle.classList.remove('active');
}
// check if current toggle is already active, if it is then disable it
// change from nextSibling to nextElementSibling due to C# rendering white space as text node
// https://stackoverflow.com/questions/33080492/c-sharp-what-is-text-node-in-htmlnode
let menu = (toggle.classList.contains('full-width')) ? toggle.parentNode.nextElementSibling : toggle.nextElementSibling;
const isDisabled = disableFilter(menu);
if (!isDisabled) {
// reset all the search filters if any is active
const selectMenu = document.querySelectorAll('.select-menu');
for (const menu of selectMenu) {
// reset all active styling on toggle
const activeToggle = menu.classList.contains('full-width')
? menu.previousElementSibling.querySelector('.select-toggle')
: menu.previousElementSibling.closest('.select-toggle');
if (activeToggle) {
activeToggle.classList.remove('active');
}
disableFilter(menu);
}
// update toggle styling to indicate active
toggle.classList.add('active');
// enable specific search filter to be active
let menu = (toggle.classList.contains('full-width')) ? toggle.parentNode.nextElementSibling : toggle.nextElementSibling;
menu.classList.add('showing');
setTimeout(() => {
menu.classList.add('show');
// enable select/deselect all buttons
const deselectAll = menu.querySelector('.deselect-all');
const selectAll = menu.querySelector('.select-all');
if (deselectAll) {
deselectAll.addEventListener('click', () => {
toggleAllOptions(false, menu);
})
}
if (selectAll) {
selectAll.addEventListener('click', () => {
toggleAllOptions(true, menu);
})
}
});
}
}
const toggleFilter = () => {
const searchFilterToggle = document.querySelectorAll('.select .select-toggle');
if (!searchFilterToggle || !searchFilterToggle.length) { return false; }
for (const toggle of searchFilterToggle) {
toggle.addEventListener('click', () => {
toggleFilterHandler(toggle);
});
}
}
const toggleAllHandler = (menu) => {
const deselectAll = menu.querySelector('.deselect-all');
const selectAll = menu.querySelector('.select-all');
if (deselectAll) {
deselectAll.addEventListener('click', () => {
toggleAllOptions(false, menu);
})
}
if (selectAll) {
selectAll.addEventListener('click', () => {
toggleAllOptions(true, menu);
})
}
}
const toggleMobileFilter = () => {
const searchFilterBtn = document.querySelector('.search-filter.mobile .search-filter-button');
const closeFilterBtn = document.querySelector('.search-filter.mobile .close-filter-sidebar');
const navLink = document.querySelectorAll('.search-filter.mobile .nav-link');
if (searchFilterBtn) {
searchFilterBtn.addEventListener('click', () => {
const filterSidebar = document.querySelector('.search-filter.mobile .filter-sidebar');
filterSidebar.classList.add('show');
const searchFilterToggle = document.querySelectorAll('.search-filter.mobile .select-menu');
if (!searchFilterToggle || !searchFilterToggle.length) { return false; }
for (const toggle of searchFilterToggle) {
toggleAllHandler(toggle);
}
});
}
if (closeFilterBtn) {
closeFilterBtn.addEventListener('click', () => {
const filterSidebar = document.querySelector('.search-filter.mobile .filter-sidebar');
filterSidebar.classList.remove('show');
});
}
// force clicking on nav to toggle menu due to desktop hover CSS causing unwanted checkbox selected when opening
// nav menu on mobile
if (navLink && navLink.length) {
for (const link of navLink) {
const parentDiv = link.closest('div');
if (parentDiv) {
parentDiv.addEventListener('click', () => {
const parentLi = parentDiv.closest('li');
parentLi.classList.toggle('active');
});
}
}
}
}
const removeFilterOption = (toggle) => {
const selectedOption = toggle.parentNode;
for (const option of selectedOption.children) {
if (option.classList.contains('active')) {
option.classList.remove('active');
}
}
}
const updateFormSelect = (toggle, selection) => {
const selectedToggle = toggle.querySelector('.selected-toggle');
let optionCode;
if (selectedToggle && selection.hasAttribute('data-option')) {
selectedToggle.textContent = selection.getAttribute('data-option');
selectedToggle.setAttribute('data-value', selectedToggle.textContent);
optionCode = selection.getAttribute('data-option-code');
}
if (selectedToggle && selectedToggle.hasAttribute('data-select-handler')) {
const pageUrlArr = window.location.href.split('?');
const pageUrl = pageUrlArr && pageUrlArr.length ? pageUrlArr[0] : null;
window.location.href = pageUrl + (optionCode ? '?location=' + optionCode : '');
}
}
const filterHandler = (e) => {
const searchFilterToggle = e.target.closest('.select-menu');
if (!searchFilterToggle) {
const activeFilterToggle = document.querySelectorAll('.select-menu.show');
if (activeFilterToggle && activeFilterToggle.length) {
for (const menu of activeFilterToggle) {
// reset all active styling on toggle
const activeToggle = menu.classList.contains('full-width')
? menu.previousElementSibling.querySelector('.select-toggle')
: menu.previousElementSibling.closest('.select-toggle');
if (activeToggle) {
activeToggle.classList.remove('active');
}
if (menu.classList.contains('full-width') && e.target.closest('.select-menu-option')) {
return false;
}
disableFilter(menu);
}
}
} else if (e.target.closest('.select-menu-option')) {
if (!e.target.closest('.full-width')) {
// remove active styling on toggle
const toggle = searchFilterToggle.previousElementSibling;
if (toggle && toggle.classList.contains('active')) {
toggle.classList.remove('active');
const errorFormField = toggle.closest('.form-field.has-error');
if (errorFormField) {
errorFormField.classList.remove('has-error');
}
}
// remove current active selection
removeFilterOption(e.target.closest('.select-menu-option'));
// highlight current option as selected in menu
e.target.closest('.select-menu-option').classList.add('active');
// closing the menu after
disableFilter(searchFilterToggle);
updateFormSelect(toggle, e.target);
}
}
}
const getMenuOptions = (filter) => {
const arr = [];
const options = filter.querySelectorAll('.select-menu-option input');
for (const option of options) {
if (option.checked === true) {
const value = option.parentNode ? option.parentNode.getAttribute('data-abbr') : null;
if (value) {
arr.push(value);
}
}
}
return arr.length ? arr : [];
}
const getMenuOption = (filter, isMobile) => {
let option;
if (isMobile) {
const radios = filter.querySelectorAll('.select-menu-option input');
for (const radio of radios) {
if (radio.checked) {
option = radio.closest('label');
break;
}
}
} else {
option = filter.querySelector('.select-menu-option.active label');
}
return option ? option.getAttribute('data-abbr') : null;
}
const getDateMenuOption = (filter) => {
let option = filter.querySelector('.select-menu-option input[type="hidden"]');
return option ? option.value : null;
}
const getFilterPayload = (isClearAll, pageSize) => {
const isMobileQuery = window.matchMedia("screen and (max-width: 576px)");
const isMobile = isMobileQuery ? isMobileQuery.matches : false;
const defaultPageSize = document.querySelector('[data-default]');
const pg = 1;
const numberOfDisplay = pageSize || (defaultPageSize ? defaultPageSize.getAttribute('data-default') : 10);
const pageUrl = window.location.href.split('?');
const searchBarInput = document.querySelector('form input[data-url]');
const pageUrlQueries = pageUrl && (pageUrl.length > 1) ? pageUrl[1].split('&') : [];
let keywordQueryArr;
let keywordQueryVal;
for (const query of pageUrlQueries) {
if (query.indexOf('keyword') > -1) {
keywordQueryArr = query.split('=');
keywordQueryVal = keywordQueryArr && keywordQueryArr.length ? keywordQueryArr[1] : '';
}
}
const keyword = searchBarInput && searchBarInput.value ? searchBarInput.value : keywordQueryVal;
const filters = isMobile
? document.querySelectorAll('.search-filter.mobile .select-menu')
: document.querySelectorAll('.search-filter.desktop .select-menu');
if (!filters || !filters.length) {
return pageSize
? {
pageUrl: pageUrl && pageUrl.length ? pageUrl[0] : null,
pg: pg,
numberOfDisplay: numberOfDisplay,
keyword: keyword,
noFilter: true
}
: null;
}
let topics = null;
let deliveryTypes = null;
let period = null;
let locations = null;
let vendors = null;
let guaranteed = null;
let publishedFrom = null;
let categories = null;
for (const filter of filters) {
const select = isMobile
? filter.previousElementSibling.querySelector('.nav-link')
: filter.previousElementSibling.querySelector('.selected-toggle');
const type = select.getAttribute('data-filter-type');
switch (type) {
case 'topic':
topics = [];
topics = getMenuOptions(filter);
break;
case 'deliveryType':
deliveryTypes = [];
deliveryTypes = getMenuOptions(filter);
break;
case 'period':
period = getMenuOption(filter, isMobile);
break;
case 'location':
locations = [];
locations = getMenuOptions(filter);
break;
case 'vendor':
vendors = [];
vendors = getMenuOptions(filter);
break;
case 'guaranteed':
guaranteed = false;
guaranteed = getMenuOption(filter, isMobile);
break;
case 'publishedFrom':
publishedFrom = getDateMenuOption(filter);
break;
case 'categories':
categories = [];
categories = getMenuOptions(filter);
break;
}
}
return {
topic: topics,
deliveryType: deliveryTypes,
period: period,
location: locations,
vendor: vendors,
guaranteed: typeof guaranteed === 'boolean' ? guaranteed.toString() : guaranteed,
pg: pg,
numberOfDisplay: numberOfDisplay,
pageUrl: pageUrl && pageUrl.length ? pageUrl[0] : null,
isClear: typeof isClearAll === 'boolean' ? isClearAll.toString() : null,
keyword: keyword,
publishedFrom: publishedFrom,
categories: categories
}
};
const urlQueryBuilder = (url, type, value) => {
const queries = url.split('?');
const operator = queries && (queries.length > 1) ? (queries[1] ? '&' : '') : '&';
if (value) {
url += operator + type + '=' + value;
}
return url;
}
const getFilterUrl = (payload) => {
let url = payload.pageUrl + '?';
if (payload.noFilter) {
url = urlQueryBuilder(url, 'pg', payload.pg || '');
url = urlQueryBuilder(url, 'numberOfDisplay', payload.numberOfDisplay || '');
url = urlQueryBuilder(url, 'keyword', payload.keyword || '');
} else {
url = urlQueryBuilder(url, 'topicStr', payload.topic ? payload.topic.join(',') : '');
url = urlQueryBuilder(url, 'deliveryTypeStr', payload.deliveryType ? payload.deliveryType.join(',') : '');
url = urlQueryBuilder(url, 'period', payload.period || '');
url = urlQueryBuilder(url, 'locationStr', payload.location ? payload.location.join(',') : '');
url = urlQueryBuilder(url, 'vendorStr', payload.vendor ? payload.vendor.join(',') : '');
url = urlQueryBuilder(url, 'guaranteed', payload.guaranteed || '');
url = urlQueryBuilder(url, 'pg', payload.pg || '');
url = urlQueryBuilder(url, 'numberOfDisplay', payload.numberOfDisplay || '');
url = urlQueryBuilder(url, 'isClear', payload.isClear || '');
url = urlQueryBuilder(url, 'keyword', payload.keyword || '');
url = urlQueryBuilder(url, 'publishedFrom', payload.publishedFrom || '');
url = urlQueryBuilder(url, 'categories', payload.categories ? payload.categories.join(',') : '');
}
return url;
}
const handleSearchFilterEvents = () => {
const isMobileQuery = window.matchMedia("screen and (max-width: 576px)");
const isMobile = isMobileQuery ? isMobileQuery.matches : false;
const applyFilters = isMobile
? document.querySelector('.search-filter.mobile [data-btn="apply-filters"]')
: document.querySelector('.search-filter.desktop [data-btn="apply-filters"]');
const clearAllFilters = isMobile
? document.querySelector('.search-filter.mobile [data-btn="clear-all-filters"]')
: document.querySelector('.search-filter.desktop [data-btn="clear-all-filters"]');
if (applyFilters) {
applyFilters.addEventListener('click', () => {
const payload = getFilterPayload(false, null);
if (payload) {
window.location.href = getFilterUrl(payload);
}
})
}
if (clearAllFilters) {
clearAllFilters.addEventListener('click', () => {
const payload = getFilterPayload(true, null);
if (payload) {
window.location.href = getFilterUrl(payload);
}
})
}
const changePageSizes = document.querySelectorAll('[data-sel="per-page"]');
if (changePageSizes && changePageSizes.length) {
for (const pageSize of changePageSizes) {
pageSize.addEventListener('change', (event) => {
const newPageSize = +(event.target['value']);
const payload = getFilterPayload(false, newPageSize);
if (payload) {
window.location.href = getFilterUrl(payload);
}
});
}
}
}