47 lines
1.6 KiB
TypeScript
47 lines
1.6 KiB
TypeScript
import { router } from '@inertiajs/react';
|
|
import { useEffect, useState } from 'react';
|
|
import { createPortal } from 'react-dom';
|
|
|
|
export function RouterProgress() {
|
|
const [progress, setProgress] = useState<number | null>(null);
|
|
|
|
useEffect(() => {
|
|
let timer: ReturnType<typeof setTimeout>;
|
|
|
|
const offStart = router.on('start', () => {
|
|
setProgress(0);
|
|
// Animate to ~80% quickly, then slow down while waiting for response
|
|
timer = setTimeout(() => setProgress(30), 50);
|
|
timer = setTimeout(() => setProgress(60), 300);
|
|
timer = setTimeout(() => setProgress(80), 800);
|
|
});
|
|
|
|
const offFinish = router.on('finish', () => {
|
|
clearTimeout(timer);
|
|
setProgress(100);
|
|
// Remove bar after fade-out
|
|
setTimeout(() => setProgress(null), 400);
|
|
});
|
|
|
|
return () => { offStart(); offFinish(); clearTimeout(timer); };
|
|
}, []);
|
|
|
|
if (progress === null) return null;
|
|
|
|
return createPortal(
|
|
<div
|
|
className="fixed top-0 left-0 z-[9999] h-[3px] pointer-events-none"
|
|
style={{
|
|
width: `${progress}%`,
|
|
background: 'linear-gradient(90deg, #D4A017, #f0c040)',
|
|
boxShadow: '0 0 8px rgba(212,160,23,0.6)',
|
|
transition: progress === 100
|
|
? 'width 0.15s ease-out, opacity 0.3s ease 0.15s'
|
|
: 'width 0.4s cubic-bezier(0.16, 1, 0.3, 1)',
|
|
opacity: progress === 100 ? 0 : 1,
|
|
}}
|
|
/>,
|
|
document.body
|
|
);
|
|
}
|