Files
gridpilot.gg/apps/website/components/media/MediaCard.tsx
2026-01-18 16:43:32 +01:00

107 lines
2.7 KiB
TypeScript

'use client';
import { Card } from '@/ui/Card';
import { Image } from '@/ui/Image';
import { ImagePlaceholder } from '@/ui/ImagePlaceholder';
import { Stack } from '@/ui/primitives/Stack';
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 (
<Stack
as={motion.div}
whileHover={{ y: -4 }}
transition={{ duration: 0.2, ease: [0.25, 0.1, 0.25, 1] }} // Fast ease-out
h="full"
gap={0}
>
<Card
bg="bg-charcoal/40"
border
borderColor="border-charcoal-outline/30"
rounded="lg"
overflow="hidden"
cursor={onClick ? 'pointer' : 'default'}
onClick={onClick}
group
h="full"
p={0}
>
<Stack position="relative" w="full" aspectRatio={aspectRatio} gap={0}>
{isLoading ? (
<ImagePlaceholder variant="loading" aspectRatio={aspectRatio} rounded="none" />
) : error ? (
<ImagePlaceholder variant="error" message={error} aspectRatio={aspectRatio} rounded="none" />
) : src ? (
<Stack w="full" h="full" overflow="hidden" gap={0}>
<Image
src={src}
alt={alt}
objectFit="cover"
fullWidth
fullHeight
/>
</Stack>
) : (
<ImagePlaceholder aspectRatio={aspectRatio} rounded="none" />
)}
{actions && (
<Stack
position="absolute"
top="2"
right="2"
direction="row"
gap={2}
opacity={0}
groupHoverOpacity={1}
transition
>
{actions}
</Stack>
)}
</Stack>
{(title || subtitle) && (
<Stack p={3} borderTop borderStyle="solid" borderColor="border-charcoal-outline/20" gap={0}>
{title && (
<Text block size="sm" weight="semibold" truncate color="text-white">
{title}
</Text>
)}
{subtitle && (
<Text block size="xs" color="text-gray-400" truncate mt={0.5}>
{subtitle}
</Text>
)}
</Stack>
)}
</Card>
</Stack>
);
}