Fix event filter transition jank and restore layout animation

This commit is contained in:
Jürgen Mummert
2026-04-05 13:46:00 +02:00
parent e1a426bde4
commit 11b927b91f
@@ -38,14 +38,9 @@
</div> </div>
<style> <style>
.event-filter-target-list {
view-transition-name: event-filter-list;
}
@media (prefers-reduced-motion: no-preference) { @media (prefers-reduced-motion: no-preference) {
::view-transition-group(event-filter-list), ::view-transition-old(root),
::view-transition-old(event-filter-list), ::view-transition-new(root) {
::view-transition-new(event-filter-list) {
animation-duration: 240ms; animation-duration: 240ms;
animation-timing-function: ease; animation-timing-function: ease;
} }
@@ -132,13 +127,14 @@
const animationMs = 220; const animationMs = 220;
let hideTimers = new WeakMap(); let hideTimers = new WeakMap();
const supportsViewTransitions = typeof document.startViewTransition === 'function'; const supportsViewTransitions = typeof document.startViewTransition === 'function';
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
let activeViewTransition = null; let activeViewTransition = null;
let isViewTransitionMutation = false; let isViewTransitionMutation = false;
let currentFilter = { type: 'all', value: '' }; let currentFilter = { type: 'all', value: '' };
let suppressedChangeEvents = 0; let suppressedChangeEvents = 0;
const runWithLayoutTransition = (mutation) => { const runWithLayoutTransition = (mutation) => {
if (!supportsViewTransitions) { if (!supportsViewTransitions || prefersReducedMotion) {
mutation(); mutation();
return; return;
} }
@@ -183,6 +179,8 @@
} }
}; };
const shouldMutateVisibilityImmediately = () => supportsViewTransitions && !prefersReducedMotion;
const hasOptionValue = (selectElement, value) => { const hasOptionValue = (selectElement, value) => {
if (!selectElement) { if (!selectElement) {
return false; return false;
@@ -315,9 +313,15 @@
} }
}; };
const showEvent = (eventItem) => { const showEvent = (eventItem, { immediateVisibility = false } = {}) => {
clearHideTimer(eventItem); clearHideTimer(eventItem);
if (immediateVisibility) {
eventItem.hidden = false;
eventItem.classList.remove('is-filtered-out');
return;
}
if (eventItem.hidden) { if (eventItem.hidden) {
eventItem.hidden = false; eventItem.hidden = false;
} }
@@ -327,8 +331,15 @@
}); });
}; };
const hideEvent = (eventItem) => { const hideEvent = (eventItem, { immediateVisibility = false } = {}) => {
clearHideTimer(eventItem); clearHideTimer(eventItem);
if (immediateVisibility) {
eventItem.classList.add('is-filtered-out');
eventItem.hidden = true;
return;
}
eventItem.classList.add('is-filtered-out'); eventItem.classList.add('is-filtered-out');
const timer = window.setTimeout(() => { const timer = window.setTimeout(() => {
@@ -425,12 +436,14 @@
currentFilter = filterState; currentFilter = filterState;
setActiveControl(filterState); setActiveControl(filterState);
const immediateVisibility = shouldMutateVisibilityImmediately();
runWithLayoutTransition(() => { runWithLayoutTransition(() => {
events.forEach((eventItem) => { events.forEach((eventItem) => {
if (matches(eventItem, filterState)) { if (matches(eventItem, filterState)) {
showEvent(eventItem); showEvent(eventItem, { immediateVisibility });
} else { } else {
hideEvent(eventItem); hideEvent(eventItem, { immediateVisibility });
} }
}); });
}); });