kontenhumas-fe/components/ui/optimized-image.tsx

85 lines
2.0 KiB
TypeScript

"use client";
import Image from "next/image";
import { useState } from "react";
import { shimmer, toBase64 } from "@/utils/globals";
interface OptimizedImageProps {
src: string;
alt: string;
width?: number;
height?: number;
className?: string;
priority?: boolean;
sizes?: string;
fill?: boolean;
quality?: number;
placeholder?: "blur" | "empty";
blurDataURL?: string;
}
const OptimizedImage: React.FC<OptimizedImageProps> = ({
src,
alt,
width = 800,
height = 600,
className = "",
priority = false,
sizes = "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw",
fill = false,
quality = 75,
placeholder = "blur",
blurDataURL,
}) => {
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(false);
const handleLoad = () => {
setIsLoading(false);
};
const handleError = () => {
setError(true);
setIsLoading(false);
};
// Generate blur data URL if not provided
const defaultBlurDataURL = blurDataURL || `data:image/svg+xml;base64,${toBase64(shimmer(width, height))}`;
if (error) {
return (
<div className={`bg-gray-200 flex items-center justify-center ${className}`}>
<span className="text-gray-500 text-sm">Image failed to load</span>
</div>
);
}
return (
<div className={`relative overflow-hidden ${className}`}>
{isLoading && (
<div className="absolute inset-0 bg-gray-200 animate-pulse" />
)}
<Image
src={src}
alt={alt}
width={fill ? undefined : width}
height={fill ? undefined : height}
className={`transition-opacity duration-300 ${
isLoading ? "opacity-0" : "opacity-100"
}`}
priority={priority}
sizes={sizes}
fill={fill}
quality={quality}
placeholder={placeholder}
blurDataURL={defaultBlurDataURL}
onLoad={handleLoad}
onError={handleError}
loading={priority ? "eager" : "lazy"}
/>
</div>
);
};
export default OptimizedImage;