/ src / components / ui / card.tsx
card.tsx
  1  import * as React from "react";
  2  
  3  import { VariantProps, cva } from "class-variance-authority";
  4  import { cn } from "../../lib/utils";
  5  
  6  const cardVariants = cva("shadow", {
  7    variants: {
  8      size: {
  9        xs: "text-xs",
 10        sm: "text-sm",
 11        md: "text-base",
 12        lg: "text-lg",
 13        xl: "text-xl",
 14      },
 15      padding: {
 16        none: "p-0",
 17        xs: "p-1",
 18        sm: "p-2",
 19        md: "p-4",
 20        lg: "p-8",
 21        xl: "p-10",
 22      },
 23      margin: {
 24        none: "m-0",
 25        xs: "m-1",
 26        sm: "m-2",
 27        md: "m-4",
 28        lg: "m-8",
 29        xl: "m-10",
 30      },
 31      color: {
 32        primary: "bg-primary/5 border-primary",
 33        secondary: "bg-secondary/5 border-secondary",
 34        error: "bg-error/5 border-error",
 35        success: "bg-success/5 border-success",
 36        warning: "bg-warning/5 border-warning",
 37        info: "bg-info/5 border-info",
 38        muted: "bg-gray-400/5 border-gray-500",
 39      },
 40      border: {
 41        0: "border-0",
 42        1: "border-1",
 43        2: "border-2",
 44        3: "border-3",
 45        4: "border-4",
 46      },
 47      round: {
 48        none: "rounded-none",
 49        both: "rounded-sm",
 50        left: "rounded-l-sm",
 51        right: "rounded-r-sm",
 52        top: "rounded-t-sm",
 53        bottom: "rounded-b-sm",
 54        topLeft: "rounded-tl-sm",
 55        topRight: "rounded-tr-sm",
 56        bottomLeft: "rounded-bl-sm",
 57        bottomRight: "rounded-br-sm",
 58      },
 59    },
 60    defaultVariants: {
 61      color: "muted",
 62      size: "md",
 63      border: 1,
 64      round: "both",
 65      padding: "none",
 66      margin: "xs",
 67    },
 68  });
 69  
 70  export interface CardProps
 71    extends Omit<React.HTMLAttributes<HTMLDivElement>, "color">,
 72      VariantProps<typeof cardVariants> {
 73    asChild?: boolean;
 74    noShadow?: boolean;
 75  }
 76  
 77  const Card = React.forwardRef<HTMLDivElement, CardProps>(
 78    (
 79      {
 80        className,
 81        size,
 82        noShadow,
 83        color,
 84        border,
 85        round,
 86        padding,
 87        margin,
 88        ...props
 89      },
 90      ref,
 91    ) => (
 92      <div
 93        ref={ref}
 94        className={cn(
 95          cardVariants({ round, size, border, color, padding, margin }),
 96          `rounded-${round}`,
 97          noShadow && "shadow-none",
 98          className,
 99        )}
100        {...props}
101      />
102    ),
103  );
104  Card.displayName = "Card";
105  
106  const CardHeader = React.forwardRef<
107    HTMLDivElement,
108    React.HTMLAttributes<HTMLDivElement>
109  >(({ className, ...props }, ref) => (
110    <div
111      ref={ref}
112      className={cn("flex flex-col space-y-1.5 p-6", className)}
113      {...props}
114    />
115  ));
116  CardHeader.displayName = "CardHeader";
117  
118  const CardTitle = React.forwardRef<
119    HTMLParagraphElement,
120    React.HTMLAttributes<HTMLHeadingElement>
121  >(({ className, ...props }, ref) => (
122    <h3
123      ref={ref}
124      className={cn("font-semibold leading-none tracking-tight", className)}
125      {...props}
126    />
127  ));
128  CardTitle.displayName = "CardTitle";
129  
130  const CardDescription = React.forwardRef<
131    HTMLParagraphElement,
132    React.HTMLAttributes<HTMLParagraphElement>
133  >(({ className, ...props }, ref) => (
134    <p
135      ref={ref}
136      className={cn("text-sm text-slate-500 dark:text-slate-400", className)}
137      {...props}
138    />
139  ));
140  CardDescription.displayName = "CardDescription";
141  
142  const CardContent = React.forwardRef<
143    HTMLDivElement,
144    React.HTMLAttributes<HTMLDivElement>
145  >(({ className, ...props }, ref) => (
146    <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
147  ));
148  CardContent.displayName = "CardContent";
149  
150  const CardFooter = React.forwardRef<
151    HTMLDivElement,
152    React.HTMLAttributes<HTMLDivElement>
153  >(({ className, ...props }, ref) => (
154    <div
155      ref={ref}
156      className={cn("flex items-center p-6 pt-0", className)}
157      {...props}
158    />
159  ));
160  CardFooter.displayName = "CardFooter";
161  
162  export {
163    Card,
164    CardContent,
165    CardDescription,
166    CardFooter,
167    CardHeader,
168    CardTitle,
169  };