Files
ursprungsverige/public/filter.js
Jonas Raneryd Imaizumi cfb35a3c38 Add badge filtering and shop category to the site
- Introduced a new 'shops' category in the build script and README.
- Implemented badge filtering in the filter functionality, allowing users to filter entries by badge.
2026-01-25 23:37:20 +01:00

213 lines
6.4 KiB
JavaScript

// Ursprung Sverige - Filtering functionality
(function() {
'use strict';
const categoryFilter = document.getElementById('category-filter');
const regionFilter = document.getElementById('region-filter');
const badgeFilter = document.getElementById('badge-filter');
const tagFilter = document.getElementById('tag-filter');
const searchInput = document.getElementById('search-input');
const clearFiltersBtn = document.getElementById('clear-filters');
const entriesGrid = document.getElementById('entries-grid');
const visibleCount = document.getElementById('visible-count');
const noResults = document.getElementById('no-results');
if (!entriesGrid) return;
const entries = Array.from(entriesGrid.querySelectorAll('.entry-card'));
// Update URL with current filter state
function updateURL() {
const params = new URLSearchParams();
if (categoryFilter?.value) {
params.set('category', categoryFilter.value);
}
if (regionFilter?.value) {
params.set('region', regionFilter.value);
}
if (badgeFilter?.value) {
params.set('badge', badgeFilter.value);
}
if (tagFilter?.value) {
params.set('tag', tagFilter.value);
}
if (searchInput?.value.trim()) {
params.set('search', searchInput.value.trim());
}
const queryString = params.toString();
const newURL = queryString
? `${window.location.pathname}?${queryString}`
: window.location.pathname;
window.history.replaceState({}, '', newURL);
}
// Apply filters from URL on page load
function applyFiltersFromURL() {
const params = new URLSearchParams(window.location.search);
if (params.has('category') && categoryFilter) {
categoryFilter.value = params.get('category');
}
if (params.has('region') && regionFilter) {
regionFilter.value = params.get('region');
}
if (params.has('badge') && badgeFilter) {
badgeFilter.value = params.get('badge');
}
if (params.has('tag') && tagFilter) {
tagFilter.value = params.get('tag');
}
if (params.has('search') && searchInput) {
searchInput.value = params.get('search');
}
filterEntries(false); // Don't update URL on initial load
}
function filterEntries(shouldUpdateURL = true) {
const categoryValue = categoryFilter?.value.toLowerCase() || '';
const regionValue = regionFilter?.value.toLowerCase() || '';
const badgeValue = badgeFilter?.value.toLowerCase() || '';
const tagValue = tagFilter?.value.toLowerCase() || '';
const searchValue = searchInput?.value.toLowerCase().trim() || '';
let visibleEntries = 0;
entries.forEach(entry => {
const entryCategory = (entry.dataset.category || '').toLowerCase();
const entryRegion = (entry.dataset.region || '').toLowerCase();
const entryBadge = (entry.dataset.badge || '').toLowerCase();
const entryTags = (entry.dataset.tags || '').toLowerCase().split(',');
const entryText = entry.textContent.toLowerCase();
const matchesCategory = !categoryValue || entryCategory === categoryValue;
const matchesRegion = !regionValue || entryRegion === regionValue;
const matchesBadge = !badgeValue || entryBadge === badgeValue;
const matchesTag = !tagValue || entryTags.includes(tagValue);
const matchesSearch = !searchValue || entryText.includes(searchValue);
const isVisible = matchesCategory && matchesRegion && matchesBadge && matchesTag && matchesSearch;
entry.hidden = !isVisible;
if (isVisible) visibleEntries++;
});
if (visibleCount) {
visibleCount.textContent = visibleEntries;
}
if (noResults) {
noResults.hidden = visibleEntries > 0;
}
if (entriesGrid) {
entriesGrid.hidden = visibleEntries === 0;
}
updateClearButtonVisibility();
if (shouldUpdateURL) {
updateURL();
}
}
function updateClearButtonVisibility() {
if (!clearFiltersBtn) return;
const hasFilters =
(categoryFilter?.value || '') !== '' ||
(regionFilter?.value || '') !== '' ||
(badgeFilter?.value || '') !== '' ||
(tagFilter?.value || '') !== '' ||
(searchInput?.value || '') !== '';
clearFiltersBtn.hidden = !hasFilters;
}
function clearFilters() {
if (categoryFilter) categoryFilter.value = '';
if (regionFilter) regionFilter.value = '';
if (badgeFilter) badgeFilter.value = '';
if (tagFilter) tagFilter.value = '';
if (searchInput) searchInput.value = '';
filterEntries(); // This will also clear the URL
}
// Handle clicking on filter buttons in cards
function handleFilterClick(e) {
const btn = e.target.closest('.filter-btn');
if (!btn) return;
const filterType = btn.dataset.filterType;
const filterValue = btn.dataset.filterValue;
if (filterType === 'category' && categoryFilter) {
categoryFilter.value = filterValue;
} else if (filterType === 'region' && regionFilter) {
regionFilter.value = filterValue;
} else if (filterType === 'badge' && badgeFilter) {
badgeFilter.value = filterValue;
} else if (filterType === 'tag' && tagFilter) {
tagFilter.value = filterValue;
}
filterEntries();
// Scroll to top to see filtered results
window.scrollTo({ top: 0, behavior: 'smooth' });
}
// Handle browser back/forward
window.addEventListener('popstate', () => {
applyFiltersFromURL();
});
// Debounce for search input
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
// Event listeners
if (categoryFilter) {
categoryFilter.addEventListener('change', () => filterEntries());
}
if (regionFilter) {
regionFilter.addEventListener('change', () => filterEntries());
}
if (badgeFilter) {
badgeFilter.addEventListener('change', () => filterEntries());
}
if (tagFilter) {
tagFilter.addEventListener('change', () => filterEntries());
}
if (searchInput) {
searchInput.addEventListener('input', debounce(() => filterEntries(), 300));
}
if (clearFiltersBtn) {
clearFiltersBtn.addEventListener('click', clearFilters);
}
// Delegate click events for filter buttons in cards
if (entriesGrid) {
entriesGrid.addEventListener('click', handleFilterClick);
}
// Apply URL filters on load
applyFiltersFromURL();
updateClearButtonVisibility();
})();