ImagePreview Component
Full-screen image preview component with navigation support for viewing transaction images.
Requirements
- 4.6: Display full-screen image preview when user clicks on uploaded image
- 4.14: Support left/right swipe navigation for switching between images
Features
- ✅ Full-screen modal overlay with dark background
- ✅ Image counter showing current position (e.g., "2 / 5")
- ✅ Previous/Next navigation buttons
- ✅ Keyboard navigation (Arrow keys, Escape)
- ✅ Touch swipe navigation for mobile devices
- ✅ Mouse drag navigation for desktop
- ✅ Circular navigation (wraps around at edges)
- ✅ Image information display (filename, file size)
- ✅ Close button and click-outside-to-close
- ✅ Body scroll lock when modal is open
- ✅ Smooth animations and transitions
- ✅ Responsive design for all screen sizes
- ✅ Accessibility support (ARIA labels, keyboard navigation)
Usage
import { ImagePreview } from './components/transaction/ImagePreview/ImagePreview';
import type { TransactionImage } from './types';
function MyComponent() {
const [previewOpen, setPreviewOpen] = useState(false);
const [previewIndex, setPreviewIndex] = useState(0);
const images: TransactionImage[] = [
{
id: 1,
transactionId: 100,
filePath: '/uploads/image1.jpg',
fileName: 'receipt1.jpg',
fileSize: 102400,
mimeType: 'image/jpeg',
createdAt: '2024-01-01T10:00:00Z',
},
// ... more images
];
const handleImageClick = (index: number) => {
setPreviewIndex(index);
setPreviewOpen(true);
};
return (
<>
{/* Thumbnail grid */}
<div className="thumbnails">
{images.map((image, index) => (
<img
key={image.id}
src={`/api/images/${image.id}`}
onClick={() => handleImageClick(index)}
/>
))}
</div>
{/* Image preview modal */}
<ImagePreview
images={images}
initialIndex={previewIndex}
open={previewOpen}
onClose={() => setPreviewOpen(false)}
/>
</>
);
}
Props
| Prop | Type | Required | Description |
|---|---|---|---|
images |
TransactionImage[] |
Yes | Array of images to preview |
initialIndex |
number |
Yes | Index of the image to display initially (0-based) |
open |
boolean |
Yes | Whether the preview modal is open |
onClose |
() => void |
Yes | Callback when the modal should close |
Navigation Methods
Keyboard
- Arrow Left: Previous image
- Arrow Right: Next image
- Escape: Close preview
Mouse
- Click navigation buttons: Navigate between images
- Click close button: Close preview
- Click overlay: Close preview
- Drag left/right: Navigate between images (desktop)
Touch
- Swipe left: Next image
- Swipe right: Previous image
- Tap close button: Close preview
- Tap overlay: Close preview
Behavior
Circular Navigation
- When on the first image, clicking "Previous" wraps to the last image
- When on the last image, clicking "Next" wraps to the first image
Body Scroll Lock
- When the preview is open, body scrolling is disabled
- Scroll is restored when the preview is closed
Index Reset
- When
initialIndexprop changes, the preview resets to that index - Useful when opening the preview from different entry points
Single Image
- Navigation buttons are hidden when there's only one image
- Swipe/drag navigation is disabled for single images
Styling
The component uses CSS custom properties for theming:
- Dark overlay:
rgba(0, 0, 0, 0.95) - Button backgrounds:
rgba(255, 255, 255, 0.1)with backdrop blur - Animations: Fade in (200ms), Zoom in (300ms)
Responsive Breakpoints
- Desktop: Full size controls
- Tablet (≤768px): Slightly smaller controls
- Mobile (≤480px): Compact controls and layout
Accessibility
- Proper ARIA roles and labels
- Keyboard navigation support
- Focus management
- Screen reader friendly
- Reduced motion support
Testing
The component includes comprehensive unit tests covering:
- Rendering in different states
- Navigation functionality (buttons, keyboard, touch)
- Close functionality
- Body scroll lock
- Index reset
- Accessibility features
Run tests:
npm test ImagePreview.test.tsx
Integration with ImageAttachment
The ImagePreview component is designed to work seamlessly with the ImageAttachment component:
import { ImageAttachment } from './components/transaction/ImageAttachment/ImageAttachment';
import { ImagePreview } from './components/transaction/ImagePreview/ImagePreview';
function TransactionForm() {
const [images, setImages] = useState<TransactionImage[]>([]);
const [previewOpen, setPreviewOpen] = useState(false);
const [previewIndex, setPreviewIndex] = useState(0);
const handlePreview = (index: number) => {
setPreviewIndex(index);
setPreviewOpen(true);
};
return (
<>
<ImageAttachment
images={images}
onAdd={handleAddImage}
onRemove={handleRemoveImage}
onPreview={handlePreview}
/>
<ImagePreview
images={images}
initialIndex={previewIndex}
open={previewOpen}
onClose={() => setPreviewOpen(false)}
/>
</>
);
}
Browser Support
- Modern browsers with ES6+ support
- Touch events for mobile devices
- Backdrop filter support (with fallback)
- CSS animations and transitions
Performance Considerations
- Images are loaded on-demand
- Smooth animations using CSS transforms
- Efficient event listeners (cleanup on unmount)
- Minimal re-renders with proper state management