geraete/components/ui/Card.tsx
2025-11-26 15:00:05 +01:00

109 lines
2.7 KiB
TypeScript

// src/components/ui/Card.tsx
'use client';
import * as React from 'react';
import clsx from 'clsx';
export type CardVariant =
| 'default' // normale Card, gerundet, Schatten
| 'edgeToEdge' // Kante-zu-Kante auf Mobile, rounded ab sm:
| 'well' // "Well" auf weißem Hintergrund
| 'wellOnGray' // Well auf grauem Hintergrund
| 'wellEdgeToEdge'; // Well, edge-to-edge auf Mobile
export interface CardProps
extends React.HTMLAttributes<HTMLDivElement> {
variant?: CardVariant;
/** Trenne Header/Body/Footer mit divide-y */
divided?: boolean;
}
function CardRoot({
variant = 'default',
divided = false,
className,
...props
}: CardProps) {
const base = 'overflow-hidden';
const variantClasses = (() => {
switch (variant) {
case 'edgeToEdge':
return 'bg-white shadow-sm sm:rounded-lg dark:bg-gray-800/50 dark:shadow-none dark:outline dark:-outline-offset-1 dark:outline-white/10';
case 'well':
return 'rounded-lg bg-gray-50 dark:bg-gray-800/50';
case 'wellOnGray':
return 'rounded-lg bg-gray-200 dark:bg-gray-800/50';
case 'wellEdgeToEdge':
return 'bg-gray-50 sm:rounded-lg dark:bg-gray-800/50';
case 'default':
default:
return 'rounded-lg bg-white shadow-sm dark:bg-gray-800/50 dark:shadow-none dark:outline dark:-outline-offset-1 dark:outline-white/10';
}
})();
const divideClasses = divided
? 'divide-y divide-gray-200 dark:divide-white/10'
: '';
return (
<div
className={clsx(base, variantClasses, divideClasses, className)}
{...props}
/>
);
}
type SectionProps = React.HTMLAttributes<HTMLDivElement> & {
/** Grau hinterlegt (Body / Footer) wie in deinen Beispielen */
muted?: boolean;
};
function CardHeader({ className, muted, ...props }: SectionProps) {
return (
<div
className={clsx(
'px-4 py-5 sm:px-6',
muted && 'bg-gray-50 dark:bg-gray-800/50',
className,
)}
{...props}
/>
);
}
function CardBody({ className, muted, ...props }: SectionProps) {
return (
<div
className={clsx(
'px-4 py-5 sm:p-6',
muted && 'bg-gray-50 dark:bg-gray-800/50',
className,
)}
{...props}
/>
);
}
function CardFooter({ className, muted, ...props }: SectionProps) {
return (
<div
className={clsx(
'px-4 py-4 sm:px-6',
muted && 'bg-gray-50 dark:bg-gray-800/50',
className,
)}
{...props}
/>
);
}
// Default-Export mit statischen Subkomponenten: <Card>, <Card.Header> etc.
export const Card = Object.assign(CardRoot, {
Header: CardHeader,
Body: CardBody,
Footer: CardFooter,
});
export default Card;