mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 00:11:13 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
90
src-old/components/common/PhotoGrid.tsx
Normal file
90
src-old/components/common/PhotoGrid.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* PhotoGrid Component
|
||||
* Reusable photo grid display with modal support
|
||||
*/
|
||||
|
||||
import { memo } from 'react';
|
||||
import { Eye, AlertCircle } from 'lucide-react';
|
||||
import { useIsMobile } from '@/hooks/use-mobile';
|
||||
import type { PhotoItem } from '@/types/photos';
|
||||
import { generatePhotoAlt } from '@/lib/photoHelpers';
|
||||
import { LazyImage } from '@/components/common/LazyImage';
|
||||
|
||||
interface PhotoGridProps {
|
||||
photos: PhotoItem[];
|
||||
onPhotoClick?: (photos: PhotoItem[], index: number) => void;
|
||||
maxDisplay?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const PhotoGrid = memo(({
|
||||
photos,
|
||||
onPhotoClick,
|
||||
maxDisplay,
|
||||
className = ''
|
||||
}: PhotoGridProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
const defaultMaxDisplay = isMobile ? 2 : 3;
|
||||
const maxToShow = maxDisplay ?? defaultMaxDisplay;
|
||||
const displayPhotos = photos.slice(0, maxToShow);
|
||||
const remainingCount = Math.max(0, photos.length - maxToShow);
|
||||
|
||||
if (photos.length === 0) {
|
||||
return (
|
||||
<div className="text-sm text-muted-foreground flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4" />
|
||||
No photos available
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`grid gap-2 ${isMobile ? 'grid-cols-2' : 'grid-cols-3'} ${className}`}>
|
||||
{displayPhotos.map((photo, index) => (
|
||||
<div
|
||||
key={photo.id}
|
||||
className="relative cursor-pointer group overflow-hidden rounded-md border bg-muted/30 h-32"
|
||||
onClick={() => onPhotoClick?.(photos, index)}
|
||||
>
|
||||
<LazyImage
|
||||
src={photo.url}
|
||||
alt={generatePhotoAlt(photo)}
|
||||
className="w-full h-32"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.style.display = 'none';
|
||||
const parent = target.parentElement;
|
||||
if (parent) {
|
||||
const errorDiv = document.createElement('div');
|
||||
errorDiv.className = 'absolute inset-0 flex flex-col items-center justify-center text-destructive text-xs p-2';
|
||||
const icon = document.createElement('div');
|
||||
icon.textContent = '⚠️';
|
||||
icon.className = 'text-lg mb-1';
|
||||
const text = document.createElement('div');
|
||||
text.textContent = 'Failed to load';
|
||||
errorDiv.appendChild(icon);
|
||||
errorDiv.appendChild(text);
|
||||
parent.appendChild(errorDiv);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-black/50 text-white opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none">
|
||||
<Eye className="w-5 h-5" />
|
||||
</div>
|
||||
{photo.caption && (
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-black/70 text-white text-xs p-1 truncate">
|
||||
{photo.caption}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{remainingCount > 0 && (
|
||||
<div className="flex items-center justify-center bg-muted rounded-md text-sm text-muted-foreground font-medium">
|
||||
+{remainingCount} more
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
PhotoGrid.displayName = 'PhotoGrid';
|
||||
Reference in New Issue
Block a user