Files
gridpilot.gg/apps/website/ui/MediaCard.tsx
2026-01-18 23:24:30 +01:00

94 lines
2.5 KiB
TypeScript

import { Box } from '@/ui/Box';
import { Card } from '@/ui/Card';
import { Image } from '@/ui/Image';
import { ImagePlaceholder } from '@/ui/ImagePlaceholder';
import { Text } from '@/ui/Text';
import { motion } from 'framer-motion';
import React from 'react';
export interface MediaCardProps {
src?: string;
alt?: string;
title?: string;
subtitle?: string;
aspectRatio?: string;
isLoading?: boolean;
error?: string;
onClick?: () => void;
actions?: React.ReactNode;
}
export function MediaCard({
src,
alt = 'Media asset',
title,
subtitle,
aspectRatio = '16/9',
isLoading,
error,
onClick,
actions,
}: MediaCardProps) {
return (
<motion.div
whileHover={{ y: -4 }}
transition={{ duration: 0.2, ease: [0.25, 0.1, 0.25, 1] }}
style={{ height: '100%' }}
>
<Card
variant="dark"
onClick={onClick}
padding={0}
style={{ height: '100%', cursor: onClick ? 'pointer' : 'default', overflow: 'hidden' }}
>
<Box position="relative" fullWidth style={{ aspectRatio }}>
{isLoading ? (
<ImagePlaceholder variant="loading" aspectRatio={aspectRatio} />
) : error ? (
<ImagePlaceholder variant="error" message={error} aspectRatio={aspectRatio} />
) : src ? (
<Box fullWidth fullHeight style={{ overflow: 'hidden' }}>
<Image
src={src}
alt={alt}
objectFit="cover"
/>
</Box>
) : (
<ImagePlaceholder aspectRatio={aspectRatio} />
)}
{actions && (
<Box
position="absolute"
top={2}
right={2}
display="flex"
gap={2}
style={{ opacity: 0, transition: 'opacity 0.2s' }}
className="group-hover:opacity-100"
>
{actions}
</Box>
)}
</Box>
{(title || subtitle) && (
<Box padding={3} style={{ borderTop: '1px solid var(--ui-color-border-muted)' }}>
{title && (
<Text block size="sm" weight="bold" truncate variant="high">
{title}
</Text>
)}
{subtitle && (
<Text block size="xs" variant="low" truncate marginTop={0.5}>
{subtitle}
</Text>
)}
</Box>
)}
</Card>
</motion.div>
);
}