alouette
Version:
A modern, customizable design system built on top of NativeWind v5 with configurable defaults
67 lines (55 loc) • 1.77 kB
text/typescript
import { useEffect, useState } from "react";
const startDelayMs = 100;
const stepIntervalMs = 500;
const completeDelayMs = 500;
const resetDelayMs = 1000;
const random = (): number => Math.ceil(Math.random() * 100) / 100;
/**
* Advances toward 100 in decreasing increments so the bar appears to slow
* down as it approaches completion, without ever reaching it on its own
* (e.g. ~20% at 100ms, ~40% at 1s, ~60% at 2s, ~80% at 3s).
*/
function nextSimulatedProgress(progress: number): number {
if (progress < 60) return progress + random() * 10 + 5;
if (progress < 70) return progress + random() * 10 + 3;
if (progress < 80) return progress + random() + 5;
if (progress < 90) return progress + random() + 1;
if (progress < 95) return progress + 0.1;
return progress;
}
export function useSimulatedProgress(loading: boolean): {
progress: number;
hidden: boolean;
} {
const [progress, setProgress] = useState(1);
const [hidden, setHidden] = useState(!loading);
useEffect(() => {
if (!loading) return undefined;
setHidden(false);
const startTimer = setTimeout(() => {
setProgress(20);
}, startDelayMs);
const stepTimer = setInterval(() => {
setProgress(nextSimulatedProgress);
}, stepIntervalMs);
return () => {
clearTimeout(startTimer);
clearInterval(stepTimer);
};
}, [loading]);
useEffect(() => {
if (loading) return undefined;
const completeTimer = setTimeout(() => {
setProgress(100);
}, completeDelayMs);
const resetTimer = setTimeout(() => {
setHidden(true);
setProgress(1);
}, resetDelayMs);
return () => {
clearTimeout(completeTimer);
clearTimeout(resetTimer);
};
}, [loading]);
return { progress, hidden };
}