Triviaverse / src /components /game /AnswerButton.tsx
Simeon Garratt
feat: redesign landing page with floating tiles, hero pillars, scoring transparency
7b22979
Raw
History Blame Contribute Delete
3.06 kB
"use client";
import { motion } from "framer-motion";
import { Check, X } from "lucide-react";
import { useCosmeticContext } from "@/components/cosmetics/CosmeticProvider";
type AnswerState = "idle" | "correct" | "wrong" | "disabled";
interface AnswerButtonProps {
text: string;
state: AnswerState;
onClick: () => void;
index: number;
showBadge?: boolean;
}
const LETTER_LABELS = ["A", "B", "C", "D"];
const stateStyles: Record<AnswerState, string> = {
idle: "glass-card border-white/20 hover:border-magenta/50 cursor-pointer",
correct: "border-green-400 bg-green-500/25 text-white shadow-[0_0_20px_rgba(34,197,94,0.4),0_0_40px_rgba(34,197,94,0.15)]",
wrong: "border-red-500 bg-red-500/25 text-red-300 animate-shake shadow-[0_0_20px_rgba(239,68,68,0.4)]",
disabled: "border-white/10 opacity-40 cursor-default",
};
const badgeStyles: Record<AnswerState, string> = {
idle: "bg-white/10",
correct: "bg-green-500 text-white",
wrong: "bg-red-500 text-white",
disabled: "bg-white/5",
};
export function AnswerButton({ text, state, onClick, index, showBadge = true }: AnswerButtonProps) {
const { equipped } = useCosmeticContext();
const hasAnswerCosmetic = equipped.answer_style !== null;
// Apply cosmetic border/hover overrides only when idle and a cosmetic is equipped
const cosmeticIdleStyle: React.CSSProperties | undefined =
state === "idle" && hasAnswerCosmetic
? { borderColor: "var(--cosmetic-answer-border)" }
: undefined;
const cosmeticHoverStyle =
state === "idle" && hasAnswerCosmetic
? {
borderColor: "var(--cosmetic-answer-hover)",
boxShadow: `0 0 20px var(--cosmetic-answer-hover)`,
}
: undefined;
return (
<motion.button
initial={{ opacity: 0, x: -10 }}
animate={
state === "correct"
? { opacity: 1, x: 0, scale: [1, 1.03, 1] }
: state === "wrong"
? { opacity: 1, x: 0 }
: { opacity: 1, x: 0 }
}
transition={
state === "correct"
? { scale: { duration: 0.3, ease: "easeOut" }, delay: index * 0.05 }
: { delay: index * 0.05 }
}
whileTap={state === "idle" ? { scale: 0.97 } : undefined}
whileHover={cosmeticHoverStyle}
className={`w-full text-left px-5 py-4 rounded-xl border-2 transition-all duration-200 ${stateStyles[state]}`}
style={cosmeticIdleStyle}
onClick={state === "idle" ? onClick : undefined}
disabled={state !== "idle"}
>
<span className="inline-flex items-center gap-3">
{showBadge && (
<span className={`flex-shrink-0 w-7 h-7 flex items-center justify-center rounded-lg text-xs font-bold transition-colors duration-200 ${badgeStyles[state]}`}>
{state === "correct" ? <Check size={14} strokeWidth={3} /> :
state === "wrong" ? <X size={14} strokeWidth={3} /> :
LETTER_LABELS[index] ?? ""}
</span>
)}
<span className="font-medium">{text}</span>
</span>
</motion.button>
);
}