'use client'; import { HTMLAttributes, ReactNode, useState } from 'react'; import { cn } from '@/lib/helper'; import Image from 'next/image'; import Collapse from './Collapse'; import { Icon } from '@iconify/react'; export interface CardProps extends Omit, 'className'> { title?: string; subtitle?: string; image?: string; imageAlt?: string; imageWidth?: number; imageHeight?: number; actions?: ReactNode; footer?: ReactNode; collapsible?: boolean; defaultCollapsed?: boolean; onCollapsedChange?: (collapsed: boolean) => void; className?: { wrapper?: string; image?: string; body?: string; title?: string; subtitle?: string; actions?: string; footer?: string; collapsible?: string; }; variant?: 'default' | 'compact' | 'bordered' | 'shadow' | 'image-full'; size?: 'sm' | 'md' | 'lg'; } const Card = ({ title, subtitle, image, imageAlt, imageWidth, imageHeight, actions, footer, collapsible, defaultCollapsed = false, onCollapsedChange, className, variant = 'default', size = 'md', children, ...props }: CardProps) => { const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed); const handleCollapsedChange = (open: boolean) => { const collapsed = !open; setIsCollapsed(collapsed); onCollapsedChange?.(collapsed); }; const getCardClasses = () => { const baseClasses = 'card bg-base-100'; const variantClasses = { default: '', compact: 'card-compact', bordered: 'border border-base-300', shadow: 'shadow-xl', 'image-full': 'card-side card-compact shadow-xl', }; const sizeClasses = { sm: 'w-64', md: 'w-96', lg: 'w-[28rem]', }; return cn( baseClasses, variantClasses[variant], variant !== 'image-full' ? sizeClasses[size] : '', className?.wrapper ); }; const getImageDimensions = () => { if (variant === 'image-full') { return { width: imageWidth || 128, height: imageHeight || 128, }; } const cardWidths = { sm: 256, // w-64 md: 384, // w-96 lg: 448, // w-[28rem] }; return { width: imageWidth || cardWidths[size], height: imageHeight || 192, }; }; const getImageClasses = () => { if (variant === 'image-full') { return cn('object-cover', className?.image); } return cn('w-full object-cover', className?.image); }; const getBodyClasses = () => { const baseClasses = 'card-body'; if (variant === 'compact' || variant === 'image-full') { return cn(baseClasses, 'p-4', className?.body); } return cn(baseClasses, 'p-6', className?.body); }; const getTitleClasses = () => { const sizeClasses = { sm: 'text-lg', md: 'text-xl', lg: 'text-2xl', }; return cn('card-title font-bold', sizeClasses[size], className?.title); }; const getSubtitleClasses = () => { return cn('text-base-content/70 text-sm mt-1', className?.subtitle); }; const getActionsClasses = () => { return cn('card-actions justify-end mt-4', className?.actions); }; const getFooterClasses = () => { return cn('border-t border-base-300 mt-4 pt-4', className?.footer); }; const renderCardContent = () => { const hasContent = children || actions || footer; const titleContent = (
{title &&

{title}

} {subtitle &&

{subtitle}

}
{collapsible && ( )}
); const cardContent = (
{children} {actions &&
{actions}
} {footer &&
{footer}
}
); return ( <> {image && (
{imageAlt
)}
{collapsible && hasContent ? ( {cardContent} ) : ( <> {(title || subtitle) && (
{title &&

{title}

} {subtitle && (

{subtitle}

)}
)} {hasContent && cardContent} )}
); }; if (variant === 'image-full' && image) { return (
{renderCardContent()}
); } return (
{renderCardContent()}
); }; export default Card;