Files
igny8/frontend/src/components/ui/card/Card.tsx
IGNY8 VPS (Salman) abc6c011ea phase 1 complete
2026-01-05 05:06:30 +00:00

222 lines
4.6 KiB
TypeScript

import { ReactNode } from "react";
import clsx from "clsx";
import Button from "../button/Button";
type CardVariant = "surface" | "panel" | "frosted" | "borderless" | "gradient";
type CardPadding = "none" | "xs" | "sm" | "md" | "lg";
type CardShadow = "none" | "sm" | "md";
interface CardProps {
children: ReactNode;
className?: string;
onClick?: () => void;
variant?: CardVariant;
padding?: CardPadding;
shadow?: CardShadow;
}
export const Card: React.FC<CardProps> = ({
children,
className = "",
onClick,
variant = "surface",
padding = "md",
shadow = "sm",
}) => {
const variantClasses: Record<CardVariant, string> = {
surface:
"bg-white border border-gray-200 dark:bg-white/[0.03] dark:border-white/10",
panel:
"bg-gray-50 border border-gray-100 dark:bg-white/[0.04] dark:border-white/[0.04]",
frosted:
"bg-white/80 backdrop-blur-xl border border-white/60 dark:bg-white/[0.08] dark:border-white/20",
borderless: "bg-transparent border border-transparent",
gradient:
"bg-[linear-gradient(180deg,var(--color-panel),var(--color-panel-alt))] border border-white/20 dark:border-white/10",
};
const paddingClasses: Record<CardPadding, string> = {
none: "p-0",
xs: "p-3",
sm: "p-4 sm:p-5",
md: "p-5 sm:p-6",
lg: "p-6 sm:p-8",
};
const shadowClasses: Record<CardShadow, string> = {
none: "",
sm: "shadow-theme-sm",
md: "shadow-theme-lg",
};
return (
<div
className={clsx(
"rounded-2xl transition-shadow duration-200",
variantClasses[variant],
paddingClasses[padding],
shadowClasses[shadow],
className,
)}
onClick={onClick}
>
{children}
</div>
);
};
interface CardImageProps {
src: string;
alt?: string;
className?: string;
}
export const CardImage: React.FC<CardImageProps> = ({
src,
alt = "card",
className = "",
}) => {
return (
<div className="mb-5 overflow-hidden rounded-lg">
<img
alt={alt}
className={`overflow-hidden rounded-lg ${className}`}
src={src}
/>
</div>
);
};
interface CardTitleProps {
children: ReactNode;
className?: string;
}
export const CardTitle: React.FC<CardTitleProps> = ({
children,
className = "",
}) => {
return (
<h4
className={`mb-1 font-medium text-gray-800 text-theme-xl dark:text-white/90 ${className}`}
>
{children}
</h4>
);
};
interface CardContentProps {
children: ReactNode;
className?: string;
}
export const CardContent: React.FC<CardContentProps> = ({
children,
className = "",
}) => {
return (
<div className={className}>
{children}
</div>
);
};
interface CardDescriptionProps {
children: ReactNode;
className?: string;
}
export const CardDescription: React.FC<CardDescriptionProps> = ({
children,
className = "",
}) => {
return (
<p className={`text-sm text-gray-500 dark:text-gray-400 ${className}`}>
{children}
</p>
);
};
interface CardActionProps {
children: ReactNode;
href?: string;
onClick?: () => void;
variant?: "button" | "link";
className?: string;
}
export const CardAction: React.FC<CardActionProps> = ({
children,
href,
onClick,
variant = "button",
className = "",
}) => {
if (variant === "button") {
return (
<div className={clsx("mt-4 inline-flex", className)}>
<Button
size="sm"
variant="solid"
tone="brand"
as={href ? "a" : "button"}
href={href}
onClick={onClick}
>
{children}
</Button>
</div>
);
}
return (
<a
href={href}
onClick={onClick}
className={clsx(
"mt-4 inline-flex items-center gap-1 text-sm font-semibold text-brand-600 hover:text-brand-700 dark:text-brand-300",
className,
)}
>
{children}
</a>
);
};
interface CardIconProps {
children: ReactNode;
className?: string;
}
export const CardIcon: React.FC<CardIconProps> = ({
children,
className = "",
}) => {
return (
<div
className={`mb-5 flex h-14 max-w-14 items-center justify-center rounded-[10.5px] bg-brand-50 text-brand-500 dark:bg-brand-500/10 ${className}`}
>
{children}
</div>
);
};
interface HorizontalCardProps {
children: ReactNode;
className?: string;
}
export const HorizontalCard: React.FC<HorizontalCardProps> = ({
children,
className = "",
}) => {
return (
<div
className={`flex flex-col gap-5 rounded-xl border border-gray-200 bg-white p-4 dark:border-gray-800 dark:bg-white/[0.03] sm:flex-row sm:items-center sm:gap-6 ${className}`}
>
{children}
</div>
);
};