Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | 9x 39x 39x 39x | import { AnimatePresence, motion } from 'framer-motion';
import React, { type ReactNode, useState } from 'react';
interface ProjectCardButtonProps {
label: string;
icon: ReactNode;
color: 'indigo' | 'rose';
onClick: () => void;
testId: string;
}
export const ProjectCardButton = React.memo(function ProjectCardButton({
label,
icon,
color,
onClick,
testId,
}: ProjectCardButtonProps) {
const [hovered, setHovered] = useState(false);
const colorMap = {
indigo: {
text: 'text-indigo-600 hover:text-indigo-700',
bg: 'from-indigo-400/20 to-indigo-500/20',
},
rose: {
text: 'text-rose-600 hover:text-rose-700',
bg: 'from-rose-400/20 to-rose-500/20',
},
};
const colors = colorMap[color];
return (
<motion.button
aria-label={`${label.toLowerCase()} project`}
data-testid={testId}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.96 }}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
onClick={onClick}
className={`relative group flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md overflow-hidden cursor-pointer transition-all duration-300 focus:outline-none ${colors.text}`}
>
<span className="relative z-10 flex items-center gap-1">
{label}
<AnimatePresence>{hovered && icon}</AnimatePresence>
</span>
<span
className={`absolute inset-0 bg-gradient-to-r ${colors.bg} opacity-0 group-hover:opacity-60 blur-md transition-opacity rounded-md`}
/>
</motion.button>
);
});
|