4.7 KiB
4.7 KiB
ExpandedImageView Component
Displays full-size images in modal overlays with loading, error, and success states.
Features
- Loading State: Purple spinner with descriptive text
- Error State: Clear error message with retry button
- Success State: Smooth fade-in transition
- Optional Metadata: Display filename, dimensions, and file size
- Responsive Padding: Adapts to mobile, tablet, and desktop
- Accessibility: ARIA live regions, semantic roles, keyboard support
Props
interface ExpandedImageViewProps {
imageUrl: string; // Required: Image URL to display
alt?: string; // Optional: Alt text (default: 'Full size view')
metadata?: { // Optional: Image metadata
filename?: string; // e.g., 'sunset_1024x768.png'
size?: string; // e.g., '2.4 MB'
dimensions?: string; // e.g., '1024 × 768'
};
showMetadata?: boolean; // Optional: Show metadata bar (default: false)
}
Usage Examples
Basic Usage
import { ExpandedImageView } from '@/components/shared/ExpandedImageView';
export default function ImageModal({ imageUrl }: { imageUrl: string }) {
return (
<div className="fixed inset-0 bg-black/90 z-50">
<ExpandedImageView
imageUrl={imageUrl}
alt="Generated landscape image"
/>
</div>
);
}
With Metadata
import { ExpandedImageView } from '@/components/shared/ExpandedImageView';
export default function ImageGalleryModal({ image }: { image: Image }) {
return (
<div className="fixed inset-0 bg-black/90 z-50">
<ExpandedImageView
imageUrl={image.url}
alt={image.prompt}
showMetadata
metadata={{
filename: image.filename,
dimensions: `${image.width} × ${image.height}`,
size: formatFileSize(image.sizeBytes)
}}
/>
</div>
);
}
In Page Provider System
'use client';
import { useState } from 'react';
import { ExpandedImageView } from '@/components/shared/ExpandedImageView';
import { CompactFooter } from '@/components/shared/CompactFooter';
export default function ImageViewerPage() {
const [selectedImage, setSelectedImage] = useState<string | null>(null);
return (
<>
{/* Gallery */}
<div className="grid grid-cols-3 gap-4 p-6">
{images.map((img) => (
<img
key={img.id}
src={img.thumbnail}
onClick={() => setSelectedImage(img.fullUrl)}
className="cursor-pointer hover:opacity-80"
/>
))}
</div>
{/* Overlay */}
{selectedImage && (
<div
className="fixed inset-0 bg-black/90 z-50 flex flex-col"
onClick={() => setSelectedImage(null)}
>
<nav className="h-16 border-b border-white/10">
<button onClick={() => setSelectedImage(null)}>Close</button>
</nav>
<main className="flex-1 overflow-auto">
<ExpandedImageView
imageUrl={selectedImage}
alt="Expanded gallery image"
showMetadata
/>
</main>
<CompactFooter />
</div>
)}
</>
);
}
Accessibility Features
ARIA Roles
- Loading State:
role="status"witharia-live="polite" - Error State:
role="alert"witharia-live="assertive" - Icon Elements:
aria-hidden="true"(decorative only)
Keyboard Support
- Retry button is keyboard accessible (Enter/Space)
- Image click event stops propagation (prevents modal close)
- Integrates with modal close handlers (Escape key)
Visual Design
- Loading: Purple spinner (
border-purple-600) matches Banatie design - Error: Red color scheme (
red-900/20,red-700/50,red-400) - Image: Shadow (
shadow-2xl) and rounded corners (rounded-lg) - Metadata: Subtle bar with Banatie card styling
Layout Constraints
- Max Image Height:
calc(100vh - 12rem)reserves space for nav/footer - Object Fit:
object-containmaintains aspect ratio - Responsive Padding:
p-4 sm:p-6 md:p-8 - Metadata Wrapping: Flexbox with
flex-wrapfor mobile
Performance
- Lazy State Management: Only renders visible state (loading/error/success)
- Event Handlers: Optimized with direct callbacks
- Image Loading: Native browser lazy loading via
onLoad/onError - Transition: CSS-only fade animation (no JavaScript)
Error Handling
- Network Failures: Catches
onerrorevents - User Retry: Resets state and triggers re-render
- Clear Messaging: Friendly error text with actionable button
- Visual Feedback: Icon and color coding for quick recognition