46 lines
1.7 KiB
JavaScript
46 lines
1.7 KiB
JavaScript
// Portfolio tag filter. The chips are plain links (?tag=...), the server filters too,
|
|
// so this only makes it instant.
|
|
export function initFilter(nav) {
|
|
const list = document.querySelector('[data-filter-list]');
|
|
const empty = document.querySelector('[data-filter-empty]');
|
|
if (!list) return;
|
|
const items = [...list.children];
|
|
const reducedMotion = matchMedia('(prefers-reduced-motion: reduce)');
|
|
|
|
function apply(tag) {
|
|
for (const chip of nav.querySelectorAll('[data-tag]')) {
|
|
if (chip.dataset.tag === tag) chip.setAttribute('aria-current', 'true');
|
|
else chip.removeAttribute('aria-current');
|
|
}
|
|
let shown = 0;
|
|
for (const item of items) {
|
|
const match = !tag || item.dataset.tags.split(' ').includes(tag);
|
|
item.hidden = !match;
|
|
if (match) {
|
|
item.classList.add('is-visible');
|
|
shown++;
|
|
}
|
|
}
|
|
if (empty) empty.hidden = shown > 0;
|
|
}
|
|
|
|
nav.addEventListener('click', (event) => {
|
|
const chip = event.target.closest('[data-tag]');
|
|
if (!chip || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
event.preventDefault();
|
|
history.replaceState(null, '', chip.href);
|
|
|
|
if (!document.startViewTransition || reducedMotion.matches) {
|
|
apply(chip.dataset.tag);
|
|
return;
|
|
}
|
|
|
|
// names only for the duration of the transition, so they don't clash with page navigation
|
|
items.forEach((item, index) => (item.style.viewTransitionName = `project-${index}`));
|
|
const transition = document.startViewTransition(() => apply(chip.dataset.tag));
|
|
transition.finished.finally(() => {
|
|
for (const item of items) item.style.viewTransitionName = '';
|
|
});
|
|
});
|
|
}
|