RYP / src /components /AnimatedLandingComponents.tsx
Soumya79's picture
Upload 1361 files
f91a684 verified
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { Terminal, CheckCircle2, CircleDashed, Cpu, Play, Zap, BookOpen, ChevronRight } from 'lucide-react';
// ── 1. Hero Code Editor ────────────────────────────────────────────────────────
const TYPING_LINES = [
{ text: 'function twoSum(nums, target) {', color: '#c084fc' },
{ text: ' const map = new Map();', color: '#93c5fd' },
{ text: ' for (let i = 0; i < nums.length; i++) {', color: '#93c5fd' },
{ text: ' const diff = target - nums[i];', color: '#93c5fd' },
{ text: ' if (map.has(diff)) {', color: '#c084fc' },
{ text: ' return [map.get(diff), i];', color: '#f87171' },
{ text: ' }', color: '#93c5fd' },
{ text: ' map.set(nums[i], i);', color: '#fbbf24' },
{ text: ' }', color: '#93c5fd' },
{ text: ' return [];', color: '#f87171' },
{ text: '}', color: '#c084fc' },
];
export function HeroCodeEditor() {
const [currentLine, setCurrentLine] = useState(0);
const [currentChar, setCurrentChar] = useState(0);
const [showToast, setShowToast] = useState(false);
useEffect(() => {
let interval: ReturnType<typeof setInterval>;
const start = setTimeout(() => {
interval = setInterval(() => {
setCurrentChar(prev => {
if (currentLine >= TYPING_LINES.length) {
clearInterval(interval);
setTimeout(() => setShowToast(true), 600);
return prev;
}
const len = TYPING_LINES[currentLine].text.length;
if (prev < len) return prev + 1;
setCurrentLine(l => l + 1);
return 0;
});
}, 28);
}, 800);
return () => { clearTimeout(start); clearInterval(interval); };
}, [currentLine]);
return (
<div className="relative w-full max-w-[500px] mx-auto">
<div className="absolute -inset-4 bg-gradient-to-r from-blue-500/20 via-violet-500/20 to-amber-500/20 blur-2xl rounded-3xl" />
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: 'easeOut' }}
className="relative rounded-2xl border border-white/10 bg-[#0d0d18]/90 backdrop-blur-xl shadow-2xl overflow-hidden flex flex-col"
style={{ height: 300 }}
>
{/* Title bar */}
<div className="flex items-center justify-between px-4 py-3 border-b border-white/5 bg-black/40">
<div className="flex gap-2">
<div className="w-3 h-3 rounded-full bg-rose-500/80" />
<div className="w-3 h-3 rounded-full bg-amber-500/80" />
<div className="w-3 h-3 rounded-full bg-emerald-500/80" />
</div>
<span className="text-[11px] font-bold text-slate-400 flex items-center gap-1.5">
<span className="text-violet-400">JS</span> twoSum.js
</span>
<div className="w-8" />
</div>
{/* Editor body */}
<div className="flex flex-1 overflow-hidden">
<div className="w-10 bg-black/20 border-r border-white/5 py-4 flex flex-col items-end pr-2 text-[11px] text-slate-600 font-mono select-none">
{TYPING_LINES.map((_, i) => <div key={i}>{i + 1}</div>)}
</div>
<div className="flex-1 p-4 font-mono text-[12px] leading-relaxed overflow-hidden">
{TYPING_LINES.map((line, idx) => {
if (idx > currentLine) return null;
const isActive = idx === currentLine;
const text = isActive ? line.text.slice(0, currentChar) : line.text;
return (
<div key={idx} className="whitespace-pre flex">
<span style={{ color: line.color }}>{text}</span>
{isActive && (
<motion.span
animate={{ opacity: [1, 0] }}
transition={{ duration: 0.7, repeat: Infinity, repeatType: 'reverse' }}
className="w-2 h-4 bg-white/70 self-center ml-0.5"
/>
)}
</div>
);
})}
</div>
</div>
{/* Toast */}
<AnimatePresence>
{showToast && (
<motion.div
initial={{ opacity: 0, y: 20, scale: 0.9 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
className="absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-3 rounded-full border border-emerald-500/30 bg-emerald-500/20 px-5 py-2.5 backdrop-blur-md z-20 shadow-lg"
>
<CheckCircle2 size={16} className="text-emerald-400" />
<span className="text-sm font-bold text-white">Accepted! 48ms</span>
</motion.div>
)}
</AnimatePresence>
</motion.div>
<motion.div
animate={{ y: [-8, 8, -8] }}
transition={{ duration: 3.5, repeat: Infinity, ease: 'easeInOut' }}
className="absolute -right-5 -top-5 flex h-12 w-12 items-center justify-center rounded-2xl border border-violet-500/30 bg-violet-500/20 backdrop-blur-md z-10"
>
<Zap size={18} className="text-violet-300" />
</motion.div>
</div>
);
}
// ── 2. Feature Glow Card ───────────────────────────────────────────────────────
export function FeatureGlowCard({ icon: Icon, title, desc, color, accent, image }: {
icon: React.ElementType; title: string; desc: string; color: string; accent: string; image: string | null;
}) {
return (
<div className="group relative flex flex-col rounded-[2rem] overflow-hidden border border-white/5 bg-[#09090f] shadow-2xl transition-all duration-300 hover:-translate-y-2">
<div className="relative h-48 w-full overflow-hidden">
{image ? (
<img src={image} alt={title} className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700" />
) : (
<div className={`w-full h-full opacity-20 bg-gradient-to-br ${color}`} />
)}
<div className="absolute inset-0 bg-gradient-to-t from-[#09090f] to-transparent opacity-90" />
</div>
<div className="p-8 pt-0 -mt-8 relative z-10">
<div className={`w-14 h-14 rounded-2xl flex items-center justify-center mb-6 shadow-lg ring-4 ring-[#09090f] bg-gradient-to-br ${color}`}>
<Icon size={24} className="text-white" />
</div>
<h3 className="text-xl font-bold mb-3 text-white">{title}</h3>
<p className="text-slate-400 text-sm leading-relaxed">{desc}</p>
</div>
</div>
);
}
// ── 3. Animated Terminal ───────────────────────────────────────────────────────
export function AnimatedTerminal() {
const [step, setStep] = useState(0);
useEffect(() => {
const seq = async () => {
setStep(1);
await new Promise(r => setTimeout(r, 600));
setStep(2);
await new Promise(r => setTimeout(r, 1200));
setStep(3);
await new Promise(r => setTimeout(r, 1500));
setStep(4);
};
const interval = setInterval(() => { setStep(0); setTimeout(seq, 500); }, 7000);
seq();
return () => clearInterval(interval);
}, []);
return (
<div className="w-full max-w-[500px] rounded-2xl border border-white/10 bg-[#0c0c16]/90 backdrop-blur-xl shadow-2xl overflow-hidden font-mono flex flex-col">
<div className="flex items-center justify-between border-b border-white/5 bg-black/40 px-4 py-3">
<div className="flex gap-2 text-xs font-bold text-slate-400">
<Terminal size={14} className="text-emerald-400" /> Test Results
</div>
<div className={`text-[10px] font-black uppercase tracking-wider px-3 py-1.5 rounded flex items-center gap-1.5 ${step >= 2 ? 'bg-slate-800/50 text-slate-400 border border-white/5' : 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30'}`}>
{step >= 2 ? <CircleDashed size={12} className="animate-spin" /> : <Play size={12} />}
{step >= 2 ? 'Evaluating...' : 'Submit Code'}
</div>
</div>
<div className="p-5 min-h-[220px] flex flex-col gap-4 text-sm relative">
<AnimatePresence>
{step === 0 && (
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
className="absolute inset-0 flex items-center justify-center text-slate-500 text-xs">
Waiting for submission...
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
{step >= 2 && (
<motion.div initial={{ opacity: 0, x: -10 }} animate={{ opacity: 1, x: 0 }} className="flex flex-col gap-3">
{['Testcase 1', 'Testcase 2', 'Testcase 3'].map((tc, i) => (
<div key={tc} className="flex justify-between items-center bg-white/5 px-3 py-2.5 rounded">
<span className="text-slate-300 font-semibold text-xs">{tc}</span>
{step >= 3
? <span className="text-emerald-400 text-[11px] font-bold flex items-center gap-1.5"><CheckCircle2 size={14} /> Passed</span>
: <span className={`text-[11px] font-bold flex items-center gap-1.5 ${i === 0 ? 'text-amber-400' : 'text-slate-600'}`}>
{i === 0 ? <><CircleDashed size={14} className="animate-spin" /> Running</> : 'Pending'}
</span>
}
</div>
))}
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
{step >= 4 && (
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
className="absolute bottom-4 left-4 right-4 flex flex-col gap-2 rounded-xl border border-emerald-500/30 bg-emerald-900/40 backdrop-blur-md p-4 shadow-lg z-10"
>
<div className="flex items-center justify-between">
<div className="font-black text-emerald-400 text-sm uppercase tracking-widest flex items-center gap-2">
<CheckCircle2 size={16} /> Accepted
</div>
<div className="text-[10px] text-emerald-400/80 font-bold tracking-wider">RUNTIME: 48MS</div>
</div>
<div className="w-full bg-black/50 h-1.5 rounded-full overflow-hidden mt-1 relative">
<motion.div
initial={{ width: 0 }} animate={{ width: '98%' }} transition={{ duration: 0.8, ease: 'easeOut' }}
className="absolute inset-y-0 left-0 bg-gradient-to-r from-emerald-500 to-emerald-300"
/>
</div>
<div className="text-[10px] text-emerald-100/70 mt-1 font-semibold">Beats 98.4% of users with JavaScript</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
);
}
// ── 4. Animated Notes Preview ──────────────────────────────────────────────────
export function AnimatedNotesPreview() {
const [step, setStep] = useState(0);
useEffect(() => {
const seq = async () => {
setStep(1);
await new Promise(r => setTimeout(r, 800));
setStep(2);
await new Promise(r => setTimeout(r, 600));
setStep(3);
await new Promise(r => setTimeout(r, 1200));
setStep(4);
await new Promise(r => setTimeout(r, 1000));
setStep(5);
};
const interval = setInterval(() => { setStep(0); setTimeout(seq, 500); }, 8000);
seq();
return () => clearInterval(interval);
}, []);
return (
<div className="w-full max-w-[500px] rounded-2xl border border-white/10 bg-[#0c0c16]/90 backdrop-blur-xl shadow-2xl overflow-hidden flex flex-col">
<div className="flex items-center justify-between border-b border-white/5 bg-black/40 px-4 py-3">
<div className="flex gap-2">
<div className="h-3 w-3 rounded-full bg-rose-500/80" />
<div className="h-3 w-3 rounded-full bg-amber-500/80" />
<div className="h-3 w-3 rounded-full bg-emerald-500/80" />
</div>
<div className="flex items-center gap-2 text-[10px] font-bold uppercase tracking-widest text-slate-500">
<BookOpen size={12} className="text-amber-400" /> OS_Notes.md
</div>
<div className="w-10" />
</div>
<div className="flex min-h-[260px] relative">
{/* Sidebar */}
<div className="w-[130px] border-r border-white/5 bg-white/[0.01] p-3 flex flex-col gap-2">
<div className="text-[10px] font-black uppercase text-slate-500 mb-2 tracking-widest">Chapters</div>
{['Virtual Memory', 'Scheduling', 'Deadlocks'].map((ch, i) => (
<div key={ch} className={`px-3 py-2 rounded text-xs font-semibold transition-colors ${step >= 1 && i === 0 ? 'bg-amber-500/10 text-amber-400 border border-amber-500/20' : 'text-slate-500'}`}>
{ch}
</div>
))}
</div>
{/* Content */}
<div className="flex-1 p-5 text-sm text-slate-300 leading-relaxed relative">
<AnimatePresence>
{step === 0 && (
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
className="absolute inset-0 flex items-center justify-center text-slate-600 font-mono text-xs">
<CircleDashed size={14} className="animate-spin mr-2" /> Loading...
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
{step >= 2 && (
<motion.h3 initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }}
className="text-xl font-black text-white mb-3 border-b border-white/10 pb-2">
Virtual Memory
</motion.h3>
)}
</AnimatePresence>
<AnimatePresence>
{step >= 3 && (
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="space-y-3">
<p className="text-[13px]">A technique that provides an <span className="text-white font-bold">idealized abstraction of storage</span>.</p>
<div className="relative inline-block mt-1">
<span className="relative z-10 font-bold text-amber-100 text-[13px]">Creates the illusion of a very large memory.</span>
{step >= 4 && (
<motion.div initial={{ width: 0 }} animate={{ width: '100%' }} transition={{ duration: 0.5 }}
className="absolute bottom-0 left-0 h-2 bg-amber-500/40 rounded-sm -z-10" />
)}
</div>
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
{step >= 5 && (
<motion.div initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }}
className="mt-5 p-3 rounded-xl border border-white/10 bg-white/5 flex gap-2 items-center justify-center">
<div className="flex flex-col gap-1 w-16 text-center">
<div className="h-5 rounded bg-blue-500/20 border border-blue-500/30 text-[9px] text-blue-400 font-bold flex items-center justify-center">Logical</div>
<div className="h-5 rounded bg-blue-500/20 border border-blue-500/30 text-[9px] text-blue-400 font-bold flex items-center justify-center">Memory</div>
</div>
<ChevronRight size={12} className="text-slate-500" />
<div className="h-12 w-14 rounded-lg bg-amber-500/20 border border-amber-500/30 text-[9px] text-amber-400 font-bold flex items-center justify-center flex-col leading-tight">
Page<br />Table
</div>
<ChevronRight size={12} className="text-slate-500" />
<div className="flex flex-col gap-1 w-16 text-center">
<div className="h-5 rounded bg-emerald-500/20 border border-emerald-500/30 text-[9px] text-emerald-400 font-bold flex items-center justify-center">Physical</div>
<div className="h-5 rounded bg-emerald-500/20 border border-emerald-500/30 text-[9px] text-emerald-400 font-bold flex items-center justify-center">RAM</div>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</div>
);
}
// ── 5. Animated Study Orb ──────────────────────────────────────────────────────
const ORB_TOPICS = [
{ label: 'DSA', color: '#3b82f6', glow: '#3b82f640', angle: 0 },
{ label: 'OS', color: '#a78bfa', glow: '#a78bfa40', angle: 72 },
{ label: 'DBMS', color: '#f59e0b', glow: '#f59e0b40', angle: 144 },
{ label: 'CN', color: '#34d399', glow: '#34d39940', angle: 216 },
{ label: 'Aptitude', color: '#f87171', glow: '#f8717140', angle: 288 },
];
const INNER_TOPICS = [
{ label: 'DP', color: '#60a5fa', angle: 36 },
{ label: 'Trees', color: '#c084fc', angle: 108 },
{ label: 'Heap', color: '#fbbf24', angle: 180 },
{ label: 'Graph', color: '#4ade80', angle: 252 },
{ label: 'Tries', color: '#fb7185', angle: 324 },
];
export function AnimatedStudyOrb() {
const cx = 200;
const cy = 200;
const outerR = 140;
const innerR = 82;
return (
<div className="relative w-full max-w-[400px] mx-auto select-none">
<div className="absolute inset-0 rounded-full blur-3xl opacity-20"
style={{ background: 'radial-gradient(circle, #3b82f6 0%, #a78bfa 50%, transparent 80%)' }} />
<svg viewBox="0 0 400 400" className="w-full h-auto" style={{ overflow: 'visible' }}>
<defs>
<radialGradient id="coreGrad" cx="50%" cy="50%" r="50%">
<stop offset="0%" stopColor="#e0f2fe" stopOpacity="1" />
<stop offset="40%" stopColor="#818cf8" stopOpacity="0.9" />
<stop offset="100%" stopColor="#1e1b4b" stopOpacity="0" />
</radialGradient>
<filter id="glow">
<feGaussianBlur stdDeviation="4" result="blur" />
<feMerge><feMergeNode in="blur" /><feMergeNode in="SourceGraphic" /></feMerge>
</filter>
</defs>
<motion.circle cx={cx} cy={cy} r={outerR}
fill="none" stroke="rgba(99,102,241,0.15)" strokeWidth="1.5" strokeDasharray="6 10"
animate={{ rotate: 360 }} transition={{ duration: 40, repeat: Infinity, ease: 'linear' }}
style={{ originX: `${cx}px`, originY: `${cy}px` }} />
<motion.circle cx={cx} cy={cy} r={innerR}
fill="none" stroke="rgba(167,139,250,0.15)" strokeWidth="1" strokeDasharray="3 8"
animate={{ rotate: -360 }} transition={{ duration: 28, repeat: Infinity, ease: 'linear' }}
style={{ originX: `${cx}px`, originY: `${cy}px` }} />
<circle cx={cx} cy={cy} r={outerR + 30} fill="none" stroke="rgba(99,102,241,0.05)" strokeWidth="1" />
<motion.circle cx={cx} cy={cy} r={44} fill="url(#coreGrad)"
animate={{ r: [42, 48, 42] }} transition={{ duration: 3, repeat: Infinity, ease: 'easeInOut' }}
filter="url(#glow)" />
<motion.circle cx={cx} cy={cy} r={30}
fill="none" stroke="rgba(224,242,254,0.25)" strokeWidth="2"
animate={{ r: [28, 34, 28], opacity: [0.3, 0.7, 0.3] }}
transition={{ duration: 2.5, repeat: Infinity, ease: 'easeInOut' }} />
<text x={cx} y={cy + 8} textAnchor="middle" fontSize="28" fill="white" opacity="0.9">📖</text>
{ORB_TOPICS.map((t, i) => {
const rad = (t.angle * Math.PI) / 180;
const nx = cx + outerR * Math.cos(rad);
const ny = cy + outerR * Math.sin(rad);
return (
<motion.g key={t.label} animate={{ rotate: 360 }}
transition={{ duration: 40, repeat: Infinity, ease: 'linear' }}
style={{ originX: `${cx}px`, originY: `${cy}px` }}>
<motion.g animate={{ rotate: -360 }}
transition={{ duration: 40, repeat: Infinity, ease: 'linear' }}
style={{ originX: `${nx}px`, originY: `${ny}px` }}>
<circle cx={nx} cy={ny} r={22} fill={t.glow} />
<motion.circle cx={nx} cy={ny} r={18} fill="#0f0f1f" stroke={t.color} strokeWidth="2"
animate={{ r: [17, 20, 17] }}
transition={{ duration: 2.5, delay: i * 0.4, repeat: Infinity, ease: 'easeInOut' }} />
<text x={nx} y={ny + 4.5} textAnchor="middle"
fontSize={t.label.length > 3 ? '7' : '9'} fontWeight="700" fill={t.color} fontFamily="monospace">
{t.label}
</text>
</motion.g>
</motion.g>
);
})}
{INNER_TOPICS.map((t, i) => {
const rad = (t.angle * Math.PI) / 180;
const nx = cx + innerR * Math.cos(rad);
const ny = cy + innerR * Math.sin(rad);
return (
<motion.g key={t.label} animate={{ rotate: -360 }}
transition={{ duration: 28, repeat: Infinity, ease: 'linear' }}
style={{ originX: `${cx}px`, originY: `${cy}px` }}>
<motion.g animate={{ rotate: 360 }}
transition={{ duration: 28, repeat: Infinity, ease: 'linear' }}
style={{ originX: `${nx}px`, originY: `${ny}px` }}>
<circle cx={nx} cy={ny} r={14} fill={`${t.color}22`} />
<motion.circle cx={nx} cy={ny} r={12} fill="#0a0a18" stroke={t.color} strokeWidth="1.5"
animate={{ r: [11, 13.5, 11] }}
transition={{ duration: 2, delay: i * 0.3, repeat: Infinity, ease: 'easeInOut' }} />
<text x={nx} y={ny + 4} textAnchor="middle"
fontSize="7" fontWeight="700" fill={t.color} fontFamily="monospace">
{t.label}
</text>
</motion.g>
</motion.g>
);
})}
</svg>
<motion.div
className="absolute top-2 right-4 px-2.5 py-1 rounded-full text-[10px] font-black text-emerald-400 border border-emerald-500/30"
style={{ background: 'rgba(10,20,15,0.85)', backdropFilter: 'blur(8px)' }}
animate={{ y: [0, -6, 0] }} transition={{ duration: 3.2, repeat: Infinity, ease: 'easeInOut' }}>
500+ problems
</motion.div>
<motion.div
className="absolute bottom-8 left-2 px-2.5 py-1 rounded-full text-[10px] font-black text-amber-400 border border-amber-500/30"
style={{ background: 'rgba(15,12,5,0.85)', backdropFilter: 'blur(8px)' }}
animate={{ y: [0, 6, 0] }} transition={{ duration: 2.8, repeat: Infinity, ease: 'easeInOut', delay: 1 }}>
FAANG mapped
</motion.div>
<motion.div
className="absolute top-1/2 right-0 -translate-y-1/2 px-2.5 py-1 rounded-full text-[10px] font-black text-blue-400 border border-blue-500/30"
style={{ background: 'rgba(5,10,20,0.85)', backdropFilter: 'blur(8px)' }}
animate={{ y: [0, -4, 0] }} transition={{ duration: 3.6, repeat: Infinity, ease: 'easeInOut', delay: 0.5 }}>
AI guided
</motion.div>
</div>
);
}