Files
gridpilot.gg/apps/website/ui/ImagePlaceholder.tsx
2026-01-18 16:18:18 +01:00

87 lines
2.2 KiB
TypeScript

import React from 'react';
import { Image as ImageIcon, AlertCircle, Loader2 } from 'lucide-react';
import { Box } from './primitives/Box';
import { Icon } from './Icon';
import { Text } from './Text';
export interface ImagePlaceholderProps {
size?: number | string;
aspectRatio?: string;
variant?: 'default' | 'error' | 'loading';
message?: string;
className?: string;
rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full';
}
export function ImagePlaceholder({
size = 'full',
aspectRatio = '1/1',
variant = 'default',
message,
className = '',
rounded = 'md',
}: ImagePlaceholderProps) {
const config = {
default: {
icon: ImageIcon,
color: 'text-gray-500',
bg: 'bg-charcoal-outline/20',
borderColor: 'border-charcoal-outline/50',
animate: undefined as 'spin' | 'pulse' | 'bounce' | 'fade-in' | 'none' | undefined,
},
error: {
icon: AlertCircle,
color: 'text-amber-500',
bg: 'bg-amber-500/5',
borderColor: 'border-amber-500/20',
animate: undefined as 'spin' | 'pulse' | 'bounce' | 'fade-in' | 'none' | undefined,
},
loading: {
icon: Loader2,
color: 'text-blue-500',
bg: 'bg-blue-500/5',
borderColor: 'border-blue-500/20',
animate: 'spin' as const,
},
};
const { icon, color, bg, borderColor, animate } = config[variant];
return (
<Box
display="flex"
flexDirection="col"
alignItems="center"
justifyContent="center"
w={typeof size === 'string' ? size : undefined}
h={typeof size === 'string' ? size : undefined}
style={typeof size === 'number' ? { width: size, height: size } : { aspectRatio }}
bg={bg}
border
borderColor={borderColor}
rounded={rounded}
className={`overflow-hidden ${className}`}
gap={2}
p={4}
>
<Icon
icon={icon}
size={6}
color={color}
animate={animate}
/>
{message && (
<Text
size="xs"
color={color}
weight="medium"
align="center"
className="max-w-[80%]"
>
{message}
</Text>
)}
</Box>
);
}