Files
maternal-app/maternal-web/components/common/OptimizedImage.tsx
2025-10-01 19:01:52 +00:00

78 lines
1.9 KiB
TypeScript

import { useState } from 'react';
import Image, { ImageProps } from 'next/image';
import { Box, Skeleton } from '@mui/material';
interface OptimizedImageProps extends Omit<ImageProps, 'onLoadingComplete'> {
onLoadComplete?: () => void;
}
/**
* OptimizedImage Component
*
* Wraps Next.js Image component with:
* - Loading states with MUI Skeleton
* - Blur placeholder support
* - Loading completion events
* - Automatic optimization
*/
export const OptimizedImage: React.FC<OptimizedImageProps> = ({
src,
alt,
width,
height,
onLoadComplete,
style,
...props
}) => {
const [isLoading, setIsLoading] = useState(true);
const handleLoadingComplete = () => {
setIsLoading(false);
onLoadComplete?.();
};
// Generate a simple blur data URL for placeholder
const blurDataURL = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI2YwZjBmMCIvPjwvc3ZnPg==';
return (
<Box
sx={{
position: 'relative',
width: typeof width === 'number' ? `${width}px` : width,
height: typeof height === 'number' ? `${height}px` : height,
...style,
}}
>
{isLoading && (
<Skeleton
variant="rectangular"
width={width}
height={height}
sx={{
position: 'absolute',
top: 0,
left: 0,
borderRadius: 'inherit',
}}
animation="wave"
/>
)}
<Image
src={src}
alt={alt}
width={width}
height={height}
onLoadingComplete={handleLoadingComplete}
placeholder="blur"
blurDataURL={blurDataURL}
style={{
opacity: isLoading ? 0 : 1,
transition: 'opacity 0.3s ease-in-out',
...style,
}}
{...props}
/>
</Box>
);
};