Files
minstrel/web/src/lib/utils/useDelayed.svelte.ts
T
bvandeusen 19d74d00d6 refactor(web): drop useDelayed sync-timeout hack; fix test timing instead
The synchronous pre-timeout in the implementation was a band-aid for a
missing flushSync() in the third test. $effect callbacks are scheduled
as microtasks and don't run until flushed — the test set source=true
before the initial effect had a chance to register the setTimeout, so
advanceTimersByTime fired nothing.

Proper fix: call flushSync() after $effect.root so the initial effect
runs synchronously, then advance fake timers. Removed the duplicate
setTimeout branch from useDelayed — one code path, no race.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 18:16:22 -04:00

38 lines
980 B
TypeScript

// Returns a rune-wrapped boolean that mirrors the source getter, but only
// flips to true after the source has been true for delayMs continuously.
// Flips back to false immediately when the source becomes false.
//
// Used to suppress flash-of-skeleton on cache-hit navigation: skeletons
// only render for loads that take longer than ~100ms.
export function useDelayed(
source: () => boolean,
delayMs = 100
): { readonly value: boolean } {
let delayed = $state(false);
let timeout: ReturnType<typeof setTimeout> | null = null;
$effect(() => {
const v = source();
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
if (v) {
timeout = setTimeout(() => {
delayed = true;
timeout = null;
}, delayMs);
} else {
delayed = false;
}
return () => {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
};
});
return { get value() { return delayed; } };
}