tahamajs/IE / Projects /front /src /components /common /LoadingSpinner.tsx
tahamajs's picture
download
raw
5.02 kB
// src/components/common/LoadingSpinner.tsx
import { motion } from "framer-motion";
import { useState, useEffect } from "react";
import { Brain, Sparkles } from "lucide-react";
interface LoadingSpinnerProps {
/** Size of the spinner */
size?: "sm" | "md" | "lg" | "xl";
/** Display full-screen overlay */
fullScreen?: boolean;
/** Loading text to display (set to empty string to hide) */
text?: string;
/** Additional CSS classes */
className?: string;
}
const sizeClasses = {
sm: "w-6 h-6",
md: "w-12 h-12",
lg: "w-20 h-20",
xl: "w-32 h-32",
};
const textSizeClasses = {
sm: "text-[10px] tracking-wider",
md: "text-xs tracking-widest uppercase",
lg: "text-sm tracking-widest uppercase font-semibold",
xl: "text-base tracking-widest uppercase font-bold",
};
export function LoadingSpinner({
size = "md",
fullScreen = false,
text = "Loading...",
className = "",
}: LoadingSpinnerProps) {
const [statusIdx, setStatusIdx] = useState(0);
const statuses = [
"Establishing secure network...",
"Retrieving peer workspace elements...",
"Syncing collaborative editors...",
"Bootstrapping AI Co-Scientist models...",
"Initializing workspace canvas..."
];
useEffect(() => {
if (!fullScreen) return;
const interval = setInterval(() => {
setStatusIdx(prev => (prev + 1) % statuses.length);
}, 1800);
return () => clearInterval(interval);
}, [fullScreen]);
const spinnerContent = (
<div className={`flex flex-col items-center justify-center gap-6 ${className}`}>
<div className={`relative ${sizeClasses[size]} flex items-center justify-center`}>
{/* Outer Ring - Slow Clockwise */}
<motion.div
animate={{ rotate: 360 }}
transition={{
duration: 2.2,
repeat: Infinity,
ease: "linear",
}}
className="absolute inset-0 rounded-full border border-transparent border-t-teal-500 border-r-indigo-500 opacity-80"
style={{ filter: "drop-shadow(0 0 8px rgba(99, 102, 241, 0.4))" }}
/>
{/* Inner Ring - Fast Counter-Clockwise */}
<motion.div
animate={{ rotate: -360 }}
transition={{
duration: 1.4,
repeat: Infinity,
ease: "linear",
}}
className="absolute inset-2 rounded-full border border-transparent border-t-pink-500 border-l-purple-500 opacity-90"
style={{ filter: "drop-shadow(0 0 6px rgba(236, 72, 153, 0.4))" }}
/>
{/* Glowing Orb Aura */}
<motion.div
animate={{ scale: [0.9, 1.15, 0.9] }}
transition={{
duration: 1.8,
repeat: Infinity,
ease: "easeInOut",
}}
className="absolute inset-4 rounded-full bg-gradient-to-tr from-teal-500/25 to-indigo-500/25 blur-sm"
/>
{/* Core Pulsing Icon */}
<motion.div
animate={{ scale: [0.85, 1.1, 0.85] }}
transition={{
duration: 1.8,
repeat: Infinity,
ease: "easeInOut",
}}
className="relative z-10 text-white flex items-center justify-center bg-slate-950 rounded-full p-2 border border-white/10"
>
<Brain className="w-5 h-5 text-teal-400" />
</motion.div>
</div>
<div className="text-center space-y-2">
{text && (
<p className={`${textSizeClasses[size]} text-indigo-900/60 dark:text-indigo-200/60 font-mono tracking-widest animate-pulse`}>
{text}
</p>
)}
{fullScreen && (
<motion.p
key={statusIdx}
initial={{ opacity: 0, y: 5 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -5 }}
className="text-[10px] text-slate-400 dark:text-slate-500 font-mono tracking-wide"
>
{statuses[statusIdx]}
</motion.p>
)}
</div>
</div>
);
if (fullScreen) {
return (
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-slate-950/85 backdrop-blur-xl">
{/* Background Decorative Mesh grid */}
<div className="absolute inset-0 bg-[linear-gradient(to_right,#8080800a_1px,transparent_1px),linear-gradient(to_bottom,#8080800a_1px,transparent_1px)] bg-[size:14px_24px] pointer-events-none" />
{/* Spotlight aura */}
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[350px] h-[350px] rounded-full bg-indigo-500/10 blur-[80px] pointer-events-none" />
<div className="relative z-10 flex flex-col items-center">
<div className="mb-6 flex items-center gap-1.5 px-3 py-1 rounded-full bg-white/5 border border-white/10 text-white text-[9px] font-bold tracking-widest uppercase">
<Sparkles className="w-3 h-3 text-teal-400" /> IE Research Network
</div>
{spinnerContent}
</div>
</div>
);
}
return spinnerContent;
}

Xet Storage Details

Size:
5.02 kB
·
Xet hash:
6e87be5d69b0ddd47d9148cfe400164dbdc7086085cbe091b256699b33485f87

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.