210 lines
6.3 KiB
JavaScript
210 lines
6.3 KiB
JavaScript
/**
|
|
* Get the status (checked/unchecked) for each filter.
|
|
*
|
|
* @returns {Object} Arrays of the name and status of each filter, grouped together into filter
|
|
* groups.
|
|
*/
|
|
function getFilterStatuses() {
|
|
const useCases = Array.from(
|
|
document.querySelectorAll('#use-case-dropdown .checkbox-container'),
|
|
).map((label) => {
|
|
return {
|
|
name: label.textContent.toLowerCase(),
|
|
isChecked: label.querySelector('input').checked,
|
|
};
|
|
});
|
|
const libraries = Array.from(
|
|
document.querySelectorAll('#library-dropdown .checkbox-container'),
|
|
).map((label) => {
|
|
return {
|
|
name: label.textContent.toLowerCase(),
|
|
isChecked: label.querySelector('input').checked,
|
|
};
|
|
});
|
|
const frameworks = Array.from(
|
|
document.querySelectorAll('#framework-dropdown .checkbox-container'),
|
|
).map((label) => {
|
|
return {
|
|
name: label.textContent.toLowerCase(),
|
|
isChecked: label.querySelector('input').checked,
|
|
};
|
|
});
|
|
const contributor = Array.from(
|
|
document.querySelectorAll('#all-examples-dropdown .checkbox-container'),
|
|
).map((label) => {
|
|
const inputElement = label.querySelector('input');
|
|
return {
|
|
name: inputElement.id.replace('-checkbox', ''),
|
|
isChecked: inputElement.checked,
|
|
};
|
|
});
|
|
return {
|
|
useCases,
|
|
libraries,
|
|
frameworks,
|
|
contributor,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Test whether the tags of the given example panel match the requested filters.
|
|
*
|
|
* @param {any} tags Tags of the example panel
|
|
* @param {any} filters Filter statuses for all the filter groups; this should be the output of
|
|
* getFilterStatuses.
|
|
* @returns {bool} True if the example panel matches the filters, or not.
|
|
*/
|
|
function panelMatchesFilters(tags, filters) {
|
|
return Object.entries(filters).every(([group, groupTags]) => {
|
|
// If there is no selection, consider the panel to be matched
|
|
if (groupTags.filter(({isChecked}) => isChecked).length === 0) {
|
|
return true;
|
|
}
|
|
|
|
// If "Any" is checked, consider the panel to be matched
|
|
if (
|
|
groupTags.filter(({name, isChecked}) => name === 'any' && isChecked)
|
|
.length > 0
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
// Otherwise show the panel if any checked item matches the tags of the panel
|
|
return groupTags
|
|
.filter(({isChecked}) => isChecked)
|
|
.some(({name}) => tags.includes(name));
|
|
});
|
|
}
|
|
|
|
/** Apply the currently selected filters to the example gallery, showing only the relevant examples. */
|
|
function applyFilter() {
|
|
const noMatchesElement = document.getElementById('no-matches');
|
|
const panels = document.querySelectorAll('.example');
|
|
const filters = getFilterStatuses();
|
|
const searchTerm = document
|
|
.getElementById('examples-search-input')
|
|
.value.toLowerCase();
|
|
|
|
// Show all panels before hiding the ones that need to be hidden.
|
|
panels.forEach((panel) => panel.classList.remove('hidden'));
|
|
|
|
// Check the title and tags of each example panel. If the tags match and the search term matches,
|
|
// show the panel.
|
|
panels.forEach((panel) => {
|
|
const title = panel
|
|
.querySelector('.example-title')
|
|
.textContent.toLowerCase();
|
|
const tags = panel
|
|
.querySelector('.example-tags')
|
|
.textContent.toLowerCase()
|
|
.concat();
|
|
const other_keywords = panel
|
|
.querySelector('.example-other-keywords')
|
|
.textContent.toLowerCase();
|
|
const keywords = `${tags} ${other_keywords}`;
|
|
const matchesSearch =
|
|
title.includes(searchTerm) || keywords.includes(searchTerm);
|
|
const matchesTags = panelMatchesFilters(keywords, filters);
|
|
|
|
// Hide panels that have no match
|
|
if (matchesSearch && matchesTags) {
|
|
panel.classList.remove('hidden');
|
|
} else {
|
|
panel.classList.add('hidden');
|
|
}
|
|
});
|
|
|
|
// If none are shown, show the "no matches" graphic.
|
|
if (document.querySelectorAll('.example:not(.hidden)').length === 0) {
|
|
noMatchesElement.classList.remove('hidden');
|
|
} else {
|
|
noMatchesElement.classList.add('hidden');
|
|
}
|
|
|
|
// Set the URL to match the active filters using query parameters.
|
|
const selectedTags = Object.entries(filters).map(([group, groupTags]) => {
|
|
return {
|
|
group,
|
|
selected: groupTags
|
|
.filter(({isChecked}) => isChecked)
|
|
.map(({name}) => name),
|
|
};
|
|
});
|
|
|
|
const queryParam = selectedTags
|
|
.filter(({group, selected}) => selected.length > 0)
|
|
.map(({group, selected}) => `${group}=${selected.join(',')}`)
|
|
.join('&');
|
|
|
|
history.replaceState(
|
|
null,
|
|
null,
|
|
queryParam.length === 0 ? location.pathname : `?${queryParam}`,
|
|
);
|
|
}
|
|
|
|
window.addEventListener('load', () => {
|
|
// Listen for filter checkbox clicks.
|
|
document.querySelectorAll('.filter-checkbox').forEach((tag) => {
|
|
tag.addEventListener('click', () => applyFilter());
|
|
});
|
|
|
|
// Add event listener for keypresses in the search bar.
|
|
document
|
|
.getElementById('examples-search-input')
|
|
.addEventListener('keyup', (event) => {
|
|
event.preventDefault();
|
|
applyFilter();
|
|
});
|
|
|
|
// Add the ability to provide URL query parameters to filter examples on page load.
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
if (urlParams.size > 0) {
|
|
urlParams.forEach((params) => {
|
|
params.split(',').forEach((param) => {
|
|
const element = document.getElementById(`${param}-checkbox`);
|
|
if (element) {
|
|
element.checked = true;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// Apply the filter in case there are URL query parameters.
|
|
applyFilter();
|
|
|
|
const dropdowns = Array.from(
|
|
document.querySelectorAll('.dropdown-content'),
|
|
).map((dropdown) => {
|
|
return {
|
|
dropdown,
|
|
input: dropdown.parentNode.querySelector('input'),
|
|
inputContainer: dropdown.parentNode,
|
|
};
|
|
});
|
|
document.addEventListener('click', (event) => {
|
|
let targetEl = event.target; // clicked element
|
|
|
|
do {
|
|
const unclicked = dropdowns.filter(({dropdown, inputContainer}) => {
|
|
return !(targetEl == dropdown || targetEl == inputContainer);
|
|
});
|
|
if (unclicked.length !== dropdowns.length) {
|
|
// There has been a click inside one of the dropdowns. Close unclicked dropdowns and return.
|
|
unclicked.forEach(({input}) => {
|
|
input.checked = false;
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Go up the DOM.
|
|
targetEl = targetEl.parentNode;
|
|
} while (targetEl);
|
|
|
|
// This is a click outside. Close all dropdowns.
|
|
dropdowns.forEach(({dropdown, input}) => {
|
|
input.checked = false;
|
|
});
|
|
});
|
|
});
|