130 lines
No EOL
5.2 KiB
TypeScript
130 lines
No EOL
5.2 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
|
|
interface UsernameModalProps {
|
|
isOpen: boolean;
|
|
currentUsername: string;
|
|
onUsernameChange: (username: string) => void;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export function UsernameModal({
|
|
isOpen,
|
|
currentUsername,
|
|
onUsernameChange,
|
|
onClose
|
|
}: UsernameModalProps) {
|
|
const [username, setUsername] = useState(currentUsername);
|
|
|
|
useEffect(() => {
|
|
setUsername(currentUsername);
|
|
}, [currentUsername]);
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (username.trim()) {
|
|
onUsernameChange(username.trim());
|
|
onClose();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<AnimatePresence>
|
|
{isOpen && (
|
|
<>
|
|
{/* Backdrop */}
|
|
<motion.div
|
|
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50"
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
onClick={onClose}
|
|
/>
|
|
|
|
{/* Modal */}
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6">
|
|
<motion.div
|
|
className="w-full max-w-[320px] sm:max-w-[350px]"
|
|
initial={{ opacity: 0, scale: 0.7, y: 30 }}
|
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
exit={{ opacity: 0, scale: 0.7, y: 30 }}
|
|
transition={{ type: "spring", stiffness: 300, damping: 25 }}
|
|
>
|
|
<form onSubmit={handleSubmit}>
|
|
<div className="bg-white/5 backdrop-blur-2xl rounded-2xl sm:rounded-3xl p-4 sm:p-6 lg:p-8 border border-white/20 shadow-2xl ring-1 ring-white/10 mx-auto">
|
|
<div className="text-center">
|
|
<motion.h3
|
|
className="text-white text-lg sm:text-xl font-bold mb-2"
|
|
initial={{ opacity: 0, y: -10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 0.1 }}
|
|
>
|
|
Choose Username
|
|
</motion.h3>
|
|
|
|
<motion.p
|
|
className="text-white/70 text-xs sm:text-sm mb-4 sm:mb-6"
|
|
initial={{ opacity: 0, y: -10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 0.15 }}
|
|
>
|
|
Your username will be shown when you place pixels
|
|
</motion.p>
|
|
|
|
<motion.div
|
|
className="mb-6 sm:mb-8"
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 0.2 }}
|
|
>
|
|
<input
|
|
type="text"
|
|
value={username}
|
|
onChange={(e) => setUsername(e.target.value)}
|
|
placeholder="Enter your username..."
|
|
className="w-full px-3 sm:px-4 py-3 sm:py-3 bg-white/5 backdrop-blur-xl border border-white/20 rounded-xl text-white text-sm sm:text-base placeholder-white/50 focus:outline-none focus:border-blue-400/60 focus:bg-white/10 transition-all duration-200 ring-1 ring-white/10 touch-manipulation"
|
|
maxLength={20}
|
|
autoFocus
|
|
/>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
className="flex gap-3 sm:gap-4"
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 0.25 }}
|
|
>
|
|
<motion.button
|
|
type="button"
|
|
className="flex-1 px-4 sm:px-6 py-3 bg-white/5 backdrop-blur-xl text-white text-sm sm:text-base rounded-xl border border-white/20 hover:bg-white/10 active:bg-white/15 transition-all duration-200 font-medium ring-1 ring-white/10 touch-manipulation"
|
|
onClick={onClose}
|
|
whileHover={{ scale: 1.02 }}
|
|
whileTap={{ scale: 0.98 }}
|
|
>
|
|
Cancel
|
|
</motion.button>
|
|
<motion.button
|
|
type="submit"
|
|
className="flex-1 px-4 sm:px-6 py-3 bg-gradient-to-r from-blue-500/80 to-purple-600/80 backdrop-blur-xl text-white text-sm sm:text-base rounded-xl hover:from-blue-600/90 hover:to-purple-700/90 active:from-blue-700/90 active:to-purple-800/90 transition-all duration-200 font-medium shadow-xl ring-1 ring-white/20 disabled:opacity-50 disabled:cursor-not-allowed touch-manipulation"
|
|
disabled={!username.trim()}
|
|
whileHover={{
|
|
scale: username.trim() ? 1.02 : 1,
|
|
boxShadow: username.trim() ? "0 10px 25px rgba(0,0,0,0.3)" : undefined
|
|
}}
|
|
whileTap={{ scale: username.trim() ? 0.98 : 1 }}
|
|
>
|
|
Save
|
|
</motion.button>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</motion.div>
|
|
</div>
|
|
</>
|
|
)}
|
|
</AnimatePresence>
|
|
);
|
|
} |