Files
photography/frontend/components/photo-modal.tsx
xujiang 3d197eb7e3 feat: setup frontend project with bun and dynamic data fetching
- Create new frontend project directory with Next.js 15 + React 19
- Migrate from pnpm to bun for faster package management
- Add TanStack Query + Axios for dynamic data fetching
- Create comprehensive Makefile with development commands
- Setup API layer with query hooks and error handling
- Configure environment variables and bun settings
- Add TypeScript type checking and project documentation
- Update CLAUDE.md with bun-specific development workflow
2025-07-08 15:28:26 +08:00

161 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useEffect } from "react"
import Image from "next/image"
import { X, ChevronLeft, ChevronRight, Camera, MapPin, Calendar } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent } from "@/components/ui/card"
interface PhotoModalProps {
photo: any
onClose: () => void
onPrev: () => void
onNext: () => void
}
export function PhotoModal({ photo, onClose, onPrev, onNext }: PhotoModalProps) {
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
switch (e.key) {
case "Escape":
onClose()
break
case "ArrowLeft":
onPrev()
break
case "ArrowRight":
onNext()
break
}
}
document.addEventListener("keydown", handleKeyDown)
document.body.style.overflow = "hidden"
return () => {
document.removeEventListener("keydown", handleKeyDown)
document.body.style.overflow = "unset"
}
}, [onClose, onPrev, onNext])
return (
<div className="fixed inset-0 z-50 bg-black/95 flex items-center justify-center p-4">
{/* Close button */}
<Button
variant="ghost"
size="sm"
className="absolute top-4 right-4 z-10 text-white hover:bg-white/10"
onClick={onClose}
>
<X className="h-6 w-6" />
</Button>
{/* Navigation buttons */}
<Button
variant="ghost"
size="sm"
className="absolute left-4 top-1/2 -translate-y-1/2 z-10 text-white hover:bg-white/10"
onClick={onPrev}
>
<ChevronLeft className="h-8 w-8" />
</Button>
<Button
variant="ghost"
size="sm"
className="absolute right-4 top-1/2 -translate-y-1/2 z-10 text-white hover:bg-white/10"
onClick={onNext}
>
<ChevronRight className="h-8 w-8" />
</Button>
<div className="w-full max-w-7xl mx-auto grid lg:grid-cols-3 gap-6 h-full max-h-[90vh]">
{/* Main image */}
<div className="lg:col-span-2 relative flex items-center justify-center">
<div className="relative w-full h-full max-h-[80vh] flex items-center justify-center">
<Image
src={photo.src || "/placeholder.svg"}
alt={photo.title}
width={1200}
height={800}
className="max-w-full max-h-full object-contain"
priority
/>
</div>
</div>
{/* Photo information */}
<div className="lg:col-span-1 overflow-y-auto">
<Card className="bg-white/95 backdrop-blur-sm">
<CardContent className="p-6">
<div className="space-y-6">
{/* Title and description */}
<div>
<h2 className="text-2xl font-light text-gray-900 mb-2">{photo.title}</h2>
<p className="text-gray-600 leading-relaxed">{photo.description}</p>
</div>
{/* Tags */}
<div>
<h3 className="text-sm font-medium text-gray-900 mb-2"></h3>
<div className="flex flex-wrap gap-2">
{photo.tags.map((tag: string) => (
<Badge key={tag} variant="secondary">
{tag}
</Badge>
))}
</div>
</div>
{/* Date and location */}
<div className="grid grid-cols-1 gap-4">
<div className="flex items-center gap-2 text-gray-600">
<Calendar className="h-4 w-4" />
<span className="text-sm">{photo.date}</span>
</div>
<div className="flex items-center gap-2 text-gray-600">
<MapPin className="h-4 w-4" />
<span className="text-sm">{photo.exif.location}</span>
</div>
</div>
{/* EXIF information */}
<div>
<h3 className="text-sm font-medium text-gray-900 mb-3 flex items-center gap-2">
<Camera className="h-4 w-4" />
</h3>
<div className="space-y-2 text-sm text-gray-600">
<div className="flex justify-between">
<span></span>
<span className="font-medium">{photo.exif.camera}</span>
</div>
<div className="flex justify-between">
<span></span>
<span className="font-medium">{photo.exif.lens}</span>
</div>
<div className="flex justify-between">
<span></span>
<span className="font-medium">{photo.exif.settings}</span>
</div>
</div>
</div>
{/* Keyboard shortcuts */}
<div className="pt-4 border-t border-gray-200">
<h3 className="text-sm font-medium text-gray-900 mb-2"></h3>
<div className="text-xs text-gray-500 space-y-1">
<div>ESC - </div>
<div> - </div>
</div>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
)
}