85 lines
2.0 KiB
TypeScript
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;
|