55 lines
1.7 KiB
TypeScript
55 lines
1.7 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
export default function Loader() {
|
|
const [done, setDone] = useState(false);
|
|
const [gone, setGone] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const hide = () => setDone(true);
|
|
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
const delay = reduced ? 0 : 380;
|
|
|
|
if (document.readyState === "complete") {
|
|
const t = setTimeout(hide, delay);
|
|
return () => clearTimeout(t);
|
|
}
|
|
|
|
const onLoad = () => setTimeout(hide, delay);
|
|
window.addEventListener("load", onLoad);
|
|
const fallback = setTimeout(hide, reduced ? 200 : 1200);
|
|
return () => {
|
|
window.removeEventListener("load", onLoad);
|
|
clearTimeout(fallback);
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!done) return;
|
|
const t = setTimeout(() => setGone(true), 500);
|
|
return () => clearTimeout(t);
|
|
}, [done]);
|
|
|
|
if (gone) return null;
|
|
|
|
return (
|
|
<div className={`loader${done ? " loader-done" : ""}`} id="loader" aria-hidden={done}>
|
|
<div className="loader-inner">
|
|
<svg className="loader-globe" viewBox="0 0 80 80" fill="none">
|
|
<circle cx="40" cy="40" r="35" stroke="url(#loaderGrad)" strokeWidth="3" strokeDasharray="60 160" className="loader-ring" />
|
|
<text x="40" y="48" textAnchor="middle" fontSize="24">🌍</text>
|
|
<defs>
|
|
<linearGradient id="loaderGrad" x1="0" y1="0" x2="80" y2="80">
|
|
<stop offset="0%" stopColor="#FF6B6B" />
|
|
<stop offset="100%" stopColor="#4ECDC4" />
|
|
</linearGradient>
|
|
</defs>
|
|
</svg>
|
|
<p className="loader-text">nomadro</p>
|
|
<div className="loader-bar"><div className="loader-progress" /></div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|