empty-state.tsx
1 import { Button } from '@/components/ui/button' 2 3 interface Props { 4 icon: React.ReactNode 5 title: string 6 subtitle?: string 7 className?: string 8 action?: { 9 label: string 10 onClick: () => void 11 } 12 } 13 14 export function EmptyState({ icon, title, subtitle, className, action }: Props) { 15 return ( 16 <div className={`flex-1 flex flex-col items-center justify-center gap-4 text-text-3 p-8 text-center group/empty ${className ?? ''}`}> 17 <div 18 className="w-14 h-14 rounded-[16px] bg-accent-soft flex items-center justify-center mb-1 relative" 19 style={{ animation: 'float 4s ease-in-out infinite' }} 20 > 21 <div className="text-accent-bright transition-transform duration-500 group-hover/empty:scale-110 group-hover/empty:rotate-[10deg]"> 22 {icon} 23 </div> 24 {/* Subtle glow background */} 25 <div className="absolute inset-0 bg-accent-bright/5 blur-xl rounded-full opacity-0 group-hover/empty:opacity-100 transition-opacity" /> 26 </div> 27 <div style={{ animation: 'fade-up 0.5s var(--ease-spring) both' }}> 28 <p className="font-display text-[15px] font-600 text-text-2">{title}</p> 29 {subtitle && <p className="text-[13px] text-text-3/50 mt-1">{subtitle}</p>} 30 </div> 31 {action && ( 32 <Button 33 variant="accent" 34 onClick={action.onClick} 35 className="mt-3 px-8 py-3 rounded-[14px] text-[14px] cursor-pointer active:scale-95 shadow-[0_4px_16px_rgba(99,102,241,0.2)] relative overflow-hidden group/btn" 36 style={{ animation: 'spring-in 0.6s var(--ease-spring) 0.2s both' }} 37 > 38 <span className="relative z-10">{action.label}</span> 39 <div 40 className="absolute inset-0 w-full h-full bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full group-hover/btn:animate-[shimmer-bar_1.5s_ease-in-out_infinite]" 41 /> 42 </Button> 43 )} 44 </div> 45 ) 46 }