Untitled

 avatar
unknown
plain_text
2 months ago
5.5 kB
2
Indexable
"use client"

import { useState, useRef, useEffect } from "react"
import Image from "next/image"
import { motion, useInView } from "framer-motion"
import { Download, Expand } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import { PhotoComments } from "@/components/photo-comments"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"

interface GalleryProps {
  images: {
    src: string
    alt: string
    width: number
    height: number
  }[]
}

function GalleryImage({ image, index, onDownload, onSelect }: any) {
  const ref = useRef(null)
  const isInView = useInView(ref, { once: true, margin: "100px" })
  const aspectRatio = image.width / image.height
  const isPortrait = aspectRatio < 0.8
  const isLandscape = aspectRatio > 1.2

  return (
    <motion.div
      ref={ref}
      initial={{ opacity: 0, y: 20 }}
      animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
      transition={{ duration: 0.5 }}
      className="relative mb-4 break-inside-avoid"
    >
      <div className={`relative overflow-hidden rounded-lg bg-muted/30 ${
        isPortrait ? 'h-[500px]' : isLandscape ? 'h-[300px]' : 'h-[400px]'
      }`}>
        <img
          src={image.src}
          alt={image.alt}
          className="h-full w-full object-cover"
          loading="lazy"
        />
        <motion.div
          initial={false}
          animate={{ opacity: 0 }}
          whileHover={{ opacity: 1 }}
          transition={{ duration: 0.2 }}
          className="absolute inset-0 flex items-center justify-center gap-2 bg-black/60"
        >
          <DialogTrigger asChild>
            <Button 
              variant="secondary" 
              size="icon" 
              className="h-9 w-9"
              onClick={() => onSelect(index)}
            >
              <Expand className="h-4 w-4" />
            </Button>
          </DialogTrigger>
          <Button 
            variant="secondary" 
            size="icon" 
            className="h-9 w-9"
            onClick={() => onDownload(image.src)}
          >
            <Download className="h-4 w-4" />
          </Button>
        </motion.div>
      </div>
    </motion.div>
  )
}

export function Gallery({ images: unsortedImages }: GalleryProps) {
  const [selectedImage, setSelectedImage] = useState<number | null>(null)
  const [isDownloading, setIsDownloading] = useState(false)

  // Sort images by filename
  const images = [...unsortedImages].sort((a, b) => {
    const fileNameA = a.src.split('/').pop() || ''
    const fileNameB = b.src.split('/').pop() || ''
    return fileNameA.localeCompare(fileNameB)
  })

  const handleDownload = async (imageSrc: string) => {
    try {
      setIsDownloading(true)
      const response = await fetch(imageSrc)
      const blob = await response.blob()
      const url = window.URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      link.download = imageSrc.split('/').pop() || 'image'
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
      window.URL.revokeObjectURL(url)
    } finally {
      setIsDownloading(false)
    }
  }

  return (
    <Dialog>
      <div className="columns-1 sm:columns-2 md:columns-3 lg:columns-4 gap-4">
        {images.map((image, index) => (
          <GalleryImage
            key={index}
            image={image}
            index={index}
            onDownload={handleDownload}
            onSelect={setSelectedImage}
          />
        ))}
      </div>

      <DialogContent className="max-w-7xl">
        <DialogHeader>
          <DialogTitle>Bildvorschau</DialogTitle>
          <DialogDescription>Ansehen und herunterladen des Bildes in voller Auflösung</DialogDescription>
        </DialogHeader>
        <Tabs defaultValue="preview">
          <div className="flex items-center justify-between mb-4">
            <TabsList>
              <TabsTrigger value="preview">Vorschau</TabsTrigger>
              <TabsTrigger value="comments">Kommentare</TabsTrigger>
            </TabsList>
            <Button 
              onClick={() => selectedImage !== null && handleDownload(images[selectedImage].src)}
              disabled={isDownloading}
            >
              <Download className="mr-2 h-4 w-4" />
              Bild herunterladen
            </Button>
          </div>
          <TabsContent value="preview">
            <div className="flex items-center justify-center overflow-hidden rounded-lg bg-background">
              {selectedImage !== null && (
                <img
                  src={images[selectedImage].src}
                  alt={images[selectedImage].alt}
                  className="max-h-[80vh] w-auto"
                  style={{
                    maxWidth: '100%',
                    objectFit: 'contain'
                  }}
                />
              )}
            </div>
          </TabsContent>
          <TabsContent value="comments">
            {selectedImage !== null && (
              <PhotoComments photoId={`photo-${selectedImage}`} />
            )}
          </TabsContent>
        </Tabs>
      </DialogContent>
    </Dialog>
  )
}
Editor is loading...
Leave a Comment