55 lines
1.3 KiB
TypeScript
55 lines
1.3 KiB
TypeScript
import { cn } from '@/lib/utils'
|
|
import { motion } from 'framer-motion'
|
|
|
|
interface CardProps {
|
|
children: React.ReactNode
|
|
className?: string
|
|
hover?: boolean
|
|
padding?: 'sm' | 'md' | 'lg'
|
|
}
|
|
|
|
export function Card({ children, className, hover = false, padding = 'lg' }: CardProps) {
|
|
const paddingClasses = {
|
|
sm: 'p-4',
|
|
md: 'p-5',
|
|
lg: 'p-6 md:p-8',
|
|
}
|
|
|
|
return (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 8 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.18, ease: 'easeOut' }}
|
|
className={cn(
|
|
'bg-surface rounded-[20px] card-shadow',
|
|
paddingClasses[padding],
|
|
hover && 'transition-shadow duration-200 hover:shadow-hover cursor-pointer',
|
|
className
|
|
)}
|
|
>
|
|
{children}
|
|
</motion.div>
|
|
)
|
|
}
|
|
|
|
interface CardHeaderProps {
|
|
title: string
|
|
subtitle?: string
|
|
action?: React.ReactNode
|
|
className?: string
|
|
}
|
|
|
|
export function CardHeader({ title, subtitle, action, className }: CardHeaderProps) {
|
|
return (
|
|
<div className={cn('flex items-start justify-between mb-6', className)}>
|
|
<div>
|
|
<h3 className="text-base font-semibold text-text-primary">{title}</h3>
|
|
{subtitle && (
|
|
<p className="text-sm text-text-secondary mt-0.5">{subtitle}</p>
|
|
)}
|
|
</div>
|
|
{action && <div>{action}</div>}
|
|
</div>
|
|
)
|
|
}
|