Add view transitions for event filter layout animation

This commit is contained in:
Jürgen Mummert
2026-04-05 13:36:58 +02:00
parent f014595bc5
commit 8a422aa4ee
@@ -37,6 +37,21 @@
<p id="eventfilter-status" class="visually-hidden" aria-live="polite"></p>
</div>
<style>
.event-filter-target-list {
view-transition-name: event-filter-list;
}
@media (prefers-reduced-motion: no-preference) {
::view-transition-group(event-filter-list),
::view-transition-old(event-filter-list),
::view-transition-new(event-filter-list) {
animation-duration: 240ms;
animation-timing-function: ease;
}
}
</style>
<script type="module">
const filters = document.getElementById('eventfilters');
@@ -116,9 +131,32 @@
const animationMs = 220;
let hideTimers = new WeakMap();
const supportsViewTransitions = typeof document.startViewTransition === 'function';
let isViewTransitionMutation = false;
let currentFilter = { type: 'all', value: '' };
let suppressedChangeEvents = 0;
const runWithLayoutTransition = (mutation) => {
if (!supportsViewTransitions) {
mutation();
return;
}
try {
document.startViewTransition(() => {
isViewTransitionMutation = true;
try {
mutation();
} finally {
isViewTransitionMutation = false;
}
});
} catch (error) {
mutation();
}
};
const hasOptionValue = (selectElement, value) => {
if (!selectElement) {
return false;
@@ -253,7 +291,16 @@
const showEvent = (eventItem) => {
clearHideTimer(eventItem);
if (eventItem.hidden) {
if (supportsViewTransitions && !isViewTransitionMutation) {
runWithLayoutTransition(() => {
eventItem.hidden = false;
});
} else {
eventItem.hidden = false;
}
}
requestAnimationFrame(() => {
eventItem.classList.remove('is-filtered-out');
@@ -265,7 +312,14 @@
eventItem.classList.add('is-filtered-out');
const timer = window.setTimeout(() => {
if (supportsViewTransitions && !isViewTransitionMutation) {
runWithLayoutTransition(() => {
eventItem.hidden = true;
});
} else {
eventItem.hidden = true;
}
hideTimers.delete(eventItem);
}, animationMs);
@@ -332,7 +386,7 @@
return;
}
const visibleCount = events.filter((eventItem) => !eventItem.hidden).length;
const visibleCount = events.filter((eventItem) => matches(eventItem, filterState)).length;
let filterText = 'alle';
if (filterState.type === 'tag' && tagSelect) {
@@ -357,6 +411,7 @@
currentFilter = filterState;
setActiveControl(filterState);
runWithLayoutTransition(() => {
events.forEach((eventItem) => {
if (matches(eventItem, filterState)) {
showEvent(eventItem);
@@ -364,6 +419,7 @@
hideEvent(eventItem);
}
});
});
updateStatus(filterState);
syncState(filterState);