51 lines
1.5 KiB
TypeScript
51 lines
1.5 KiB
TypeScript
type CompletionId = string
|
|
|
|
type CompletionTimer = number
|
|
type CompletionFrame = number
|
|
|
|
export const COMPLETION_EXIT_DURATION = 320
|
|
|
|
export function shouldAnimateCompletionExit({ completing, showCompleted }: { completing: boolean; showCompleted: boolean }) {
|
|
return completing && !showCompleted
|
|
}
|
|
|
|
export function completionExitDelay(reducedMotion: boolean) {
|
|
return reducedMotion ? 0 : COMPLETION_EXIT_DURATION
|
|
}
|
|
|
|
export function prefersReducedMotion() {
|
|
return window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false
|
|
}
|
|
|
|
export function waitForCompletionExit(reducedMotion = prefersReducedMotion()) {
|
|
const delay = completionExitDelay(reducedMotion)
|
|
return delay ? new Promise<void>((resolve) => window.setTimeout(resolve, delay)) : Promise.resolve()
|
|
}
|
|
|
|
export function createCompletionPulse(
|
|
activate: (id: CompletionId) => void,
|
|
deactivate: (id: CompletionId) => void,
|
|
duration = 420,
|
|
) {
|
|
const timers = new Map<CompletionId, CompletionTimer>()
|
|
const frames = new Map<CompletionId, CompletionFrame>()
|
|
|
|
return (id: CompletionId) => {
|
|
const previousTimer = timers.get(id)
|
|
if (previousTimer !== undefined) window.clearTimeout(previousTimer)
|
|
const previousFrame = frames.get(id)
|
|
if (previousFrame !== undefined) window.cancelAnimationFrame(previousFrame)
|
|
deactivate(id)
|
|
|
|
const frame = window.requestAnimationFrame(() => {
|
|
frames.delete(id)
|
|
activate(id)
|
|
timers.set(id, window.setTimeout(() => {
|
|
deactivate(id)
|
|
timers.delete(id)
|
|
}, duration))
|
|
})
|
|
frames.set(id, frame)
|
|
}
|
|
}
|