110 lines
4.2 KiB
JavaScript
110 lines
4.2 KiB
JavaScript
'use client';
|
|
|
|
import React from 'react';
|
|
|
|
const Card = ({
|
|
children,
|
|
title,
|
|
subtitle,
|
|
header,
|
|
footer,
|
|
variant = 'default',
|
|
padding = 'md',
|
|
hover = false,
|
|
spacing = 'md',
|
|
className = '',
|
|
...props
|
|
}) => {
|
|
const baseClassName = 'border transition-all duration-[120ms] ease-out';
|
|
const isLightDark = variant === 'lightDark';
|
|
|
|
const variants = {
|
|
default: 'rounded-xl bg-white dark:bg-[#0B0B0B] border-neutral-200 dark:border-[#1B1B1B]',
|
|
elevated: 'rounded-xl bg-neutral-50/80 dark:bg-neutral-900/40 border-neutral-200 dark:border-neutral-800/50',
|
|
outline: 'rounded-xl bg-transparent border-neutral-300 dark:border-neutral-700/50',
|
|
solid: 'rounded-xl bg-neutral-100 dark:bg-neutral-800 border-neutral-200 dark:border-neutral-700',
|
|
lightDark: 'rounded-xl bg-white dark:bg-neutral-900/40 border-neutral-200 dark:border-neutral-800/50',
|
|
success: 'rounded-xl bg-green-700/10 dark:bg-green-600/10 border-green-800/30 dark:border-green-600/20',
|
|
info: 'rounded-xl bg-blue-700/10 dark:bg-blue-600/10 border-blue-800/30 dark:border-blue-600/20',
|
|
warning: 'rounded-xl bg-yellow-700/10 dark:bg-yellow-600/10 border-yellow-800/30 dark:border-yellow-600/20',
|
|
danger: 'rounded-xl bg-red-700/10 dark:bg-red-600/10 border-red-800/30 dark:border-red-600/20'
|
|
};
|
|
|
|
const variantsHover = {
|
|
default: 'hover:bg-neutral-50 dark:hover:bg-neutral-800/40 hover:border-neutral-300 dark:hover:border-neutral-700/50',
|
|
elevated: 'hover:bg-neutral-100 dark:hover:bg-neutral-900/50 hover:border-neutral-300 dark:hover:border-neutral-700/50',
|
|
outline: 'hover:border-neutral-400 dark:hover:border-neutral-600/50',
|
|
solid: 'hover:bg-neutral-200 dark:hover:bg-neutral-700/80',
|
|
lightDark: 'hover:bg-neutral-50/90 hover:border-neutral-300/80 dark:hover:bg-neutral-900/50 dark:hover:border-neutral-700/50',
|
|
success: 'hover:bg-green-700/15 dark:hover:bg-green-600/15 hover:border-green-800/40 dark:hover:border-green-600/30',
|
|
info: 'hover:bg-blue-700/15 dark:hover:bg-blue-600/15 hover:border-blue-800/40 dark:hover:border-blue-600/30',
|
|
warning: 'hover:bg-yellow-700/15 dark:hover:bg-yellow-600/15 hover:border-yellow-800/40 dark:hover:border-yellow-600/30',
|
|
danger: 'hover:bg-red-700/15 dark:hover:bg-red-600/15 hover:border-red-800/40 dark:hover:border-red-600/30'
|
|
};
|
|
|
|
const paddings = {
|
|
none: '',
|
|
sm: 'p-4',
|
|
md: 'p-6',
|
|
lg: 'p-8'
|
|
};
|
|
|
|
const spacings = {
|
|
none: '',
|
|
sm: 'flex flex-col gap-2',
|
|
md: 'flex flex-col gap-4',
|
|
lg: 'flex flex-col gap-6'
|
|
};
|
|
|
|
const headerBorderClass = 'border-neutral-200 dark:border-[#1B1B1B]';
|
|
const footerBorderClass = 'border-neutral-200 dark:border-[#1B1B1B]';
|
|
const titleClass = 'text-sm font-medium text-neutral-900 dark:text-white';
|
|
const subtitleClass = 'text-xs text-neutral-500 dark:text-neutral-400 mt-1';
|
|
|
|
const CardHeader = () => {
|
|
if (header) return header;
|
|
if (!title && !subtitle) return null;
|
|
|
|
return (
|
|
<div className={`border-b ${headerBorderClass} pb-4 mb-6`}>
|
|
{title && (
|
|
<h3 className={titleClass}>
|
|
{title}
|
|
</h3>
|
|
)}
|
|
{subtitle && (
|
|
<p className={subtitleClass}>
|
|
{subtitle}
|
|
</p>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const CardFooter = () => {
|
|
if (!footer) return null;
|
|
|
|
return (
|
|
<div className={`border-t ${footerBorderClass} pt-4 mt-6`}>
|
|
{footer}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={`${baseClassName} ${variants[variant] || variants.default} ${hover ? (variantsHover[variant] || variantsHover.default) : ''} ${className}`}
|
|
{...props}
|
|
>
|
|
<div className={paddings[padding]}>
|
|
<CardHeader />
|
|
<div className={spacings[spacing]}>
|
|
{children}
|
|
</div>
|
|
<CardFooter />
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Card;
|