All files / src/components card.tsx

96.15% Statements 25/26
50% Branches 1/2
100% Functions 6/6
100% Lines 25/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143              11x   11x   135x                         135x                 11x   11x   64x             64x                 11x   11x         113x   113x                         11x   11x       3x         3x               11x   11x   73x         73x                 11x   11x   3x             3x                 11x                    
import type { HTMLAttributes } from 'react';
import { forwardRef } from 'react';
 
import { cn } from '@/lib/utils';
import { Heading } from '@/components/heading';
 
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
('use memo');
 
const Card = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  ({ className, ...props }, ref) => {
    const cardStyles = [
      // Appearance
      'bg-card border-2',
      // Border
      'border-border',
      // Border radius
      'rounded-[10px]',
      // Typography
      'text-card-foreground',
      // Transitions
      'transition-colors',
    ];
 
    return (
      <div
        ref={ref}
        className={cn(cardStyles.join(' '), className)}
        {...props}
      />
    );
  }
);
Card.displayName = 'Card';
 
const CardHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  ({ className, ...props }, ref) => {
    const headerStyles = [
      // Layout
      'flex flex-col',
      // Spacing
      'space-y-1.5 p-6',
    ];
 
    return (
      <div
        ref={ref}
        className={cn(headerStyles.join(' '), className)}
        {...props}
      />
    );
  }
);
CardHeader.displayName = 'CardHeader';
 
const CardTitle = forwardRef<
  HTMLParagraphElement,
  HTMLAttributes<HTMLHeadingElement>
>(({ className, children, ...props }, ref) => {
  // Only render if there's content
  Iif (!children) return null;
 
  return (
    <Heading
      as="h3"
      size="h6"
      variant="muted"
      ref={ref}
      className={cn('leading-none tracking-tight', className)}
      {...props}
    >
      {children}
    </Heading>
  );
});
CardTitle.displayName = 'CardTitle';
 
const CardDescription = forwardRef<
  HTMLParagraphElement,
  HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
  const descriptionStyles = [
    // Typography
    'text-sm text-muted-foreground',
  ];
 
  return (
    <p
      ref={ref}
      className={cn(descriptionStyles.join(' '), className)}
      {...props}
    />
  );
});
CardDescription.displayName = 'CardDescription';
 
const CardContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  ({ className, ...props }, ref) => {
    const contentStyles = [
      // Spacing
      'p-6',
    ];
 
    return (
      <div
        ref={ref}
        className={cn(contentStyles.join(' '), className)}
        {...props}
      />
    );
  }
);
CardContent.displayName = 'CardContent';
 
const CardFooter = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
  ({ className, ...props }, ref) => {
    const footerStyles = [
      // Layout
      'flex items-center',
      // Spacing
      'p-6',
    ];
 
    return (
      <div
        ref={ref}
        className={cn(footerStyles.join(' '), className)}
        {...props}
      />
    );
  }
);
CardFooter.displayName = 'CardFooter';
 
export {
  Card,
  CardHeader,
  CardFooter,
  CardTitle,
  CardDescription,
  CardContent,
};