import React from 'react'; import Image from 'next/image'; import { cn } from '../../lib/utils'; import { getViewport, generateImageSizes, getOptimalImageQuality } from '../../lib/responsive'; // Aspect ratio options type AspectRatio = '1:1' | '4:3' | '16:9' | '21:9' | 'auto'; // Size options type ImageSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'; interface FeaturedImageProps { src: string; alt: string; width?: number; height?: number; aspectRatio?: AspectRatio; size?: ImageSize; caption?: string; priority?: boolean; className?: string; objectFit?: 'cover' | 'contain' | 'fill'; lazy?: boolean; // Responsive props responsiveSrc?: { mobile?: string; tablet?: string; desktop?: string; }; // Quality optimization quality?: number | 'auto'; // Placeholder placeholder?: 'blur' | 'empty'; blurDataURL?: string; } // Helper function to get aspect ratio classes const getAspectRatio = (ratio: AspectRatio) => { switch (ratio) { case '1:1': return 'aspect-square'; case '4:3': return 'aspect-[4/3]'; case '16:9': return 'aspect-video'; case '21:9': return 'aspect-[21/9]'; case 'auto': return 'aspect-auto'; default: return 'aspect-auto'; } }; // Helper function to get size classes const getSizeStyles = (size: ImageSize) => { switch (size) { case 'sm': return 'max-w-xs'; case 'md': return 'max-w-md'; case 'lg': return 'max-w-lg'; case 'xl': return 'max-w-xl'; case 'full': return 'max-w-full'; default: return 'max-w-lg'; } }; export const FeaturedImage: React.FC = ({ src, alt, width, height, aspectRatio = 'auto', size = 'md', caption, priority = false, className = '', objectFit = 'cover', lazy = true, responsiveSrc, quality = 'auto', placeholder = 'empty', blurDataURL, }) => { const hasDimensions = width && height; const shouldLazyLoad = !priority && lazy; // Get responsive image source const getResponsiveSrc = () => { if (responsiveSrc) { if (typeof window === 'undefined') return responsiveSrc.mobile || src; const viewport = getViewport(); if (viewport.isMobile && responsiveSrc.mobile) return responsiveSrc.mobile; if (viewport.isTablet && responsiveSrc.tablet) return responsiveSrc.tablet; if (viewport.isDesktop && responsiveSrc.desktop) return responsiveSrc.desktop; } return src; }; // Get optimal quality const getQuality = () => { if (quality === 'auto') { if (typeof window === 'undefined') return 75; const viewport = getViewport(); return getOptimalImageQuality(viewport); } return quality; }; // Generate responsive sizes attribute const getSizes = () => { const baseSizes = generateImageSizes(); // Adjust based on component size prop switch (size) { case 'sm': return '(max-width: 640px) 50vw, (max-width: 768px) 33vw, 25vw'; case 'md': return '(max-width: 640px) 75vw, (max-width: 768px) 50vw, 33vw'; case 'lg': return baseSizes; case 'xl': return '(max-width: 640px) 100vw, (max-width: 768px) 75vw, 50vw'; case 'full': return '100vw'; default: return baseSizes; } }; const responsiveImageSrc = getResponsiveSrc(); const optimalQuality = getQuality(); return (
{alt}
{caption && (
{caption}
)}
); }; // Sub-components for common image patterns export const Avatar: React.FC<{ src: string; alt: string; size?: 'sm' | 'md' | 'lg'; className?: string; }> = ({ src, alt, size = 'md', className = '' }) => { const sizeClasses = { sm: 'w-8 h-8', md: 'w-12 h-12', lg: 'w-16 h-16', }[size]; return (
{alt}
); }; export const ImageGallery: React.FC<{ images: Array<{ src: string; alt: string; caption?: string; }>; cols?: 2 | 3 | 4; className?: string; }> = ({ images, cols = 3, className = '' }) => { const colClasses = { 2: 'grid-cols-1 md:grid-cols-2', 3: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3', 4: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4', }[cols]; return (
{images.map((image, index) => ( ))}
); }; export default FeaturedImage;