{"version":3,"sources":["../src/image-gallery/image-gallery.tsx"],"names":["cva","React","jsxs","Fragment","jsx","cn","ZoomIn","Download","Trash2","X","ChevronLeft","ChevronRight","formatBytes","ExternalLink"],"mappings":"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","file":"chunk-4UVP67CT.cjs","sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { ChevronLeft, ChevronRight, Download, ExternalLink, Trash2, X, ZoomIn } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn, formatBytes } from \"../lib/utils\";\n\nconst galleryVariants = cva(\"grid gap-4\", {\n  variants: {\n    columns: {\n      2: \"grid-cols-2\",\n      3: \"grid-cols-2 sm:grid-cols-3\",\n      4: \"grid-cols-2 sm:grid-cols-3 lg:grid-cols-4\",\n      5: \"grid-cols-2 sm:grid-cols-3 lg:grid-cols-5\",\n      auto: \"grid-cols-[repeat(auto-fill,minmax(200px,1fr))]\",\n    },\n  },\n  defaultVariants: {\n    columns: 4,\n  },\n});\n\nconst imageVariants = cva(\n  \"relative rounded-lg overflow-hidden bg-muted cursor-pointer group transition-transform hover:scale-[1.02]\",\n  {\n    variants: {\n      aspect: {\n        square: \"aspect-square\",\n        video: \"aspect-video\",\n        auto: \"\",\n      },\n    },\n    defaultVariants: {\n      aspect: \"square\",\n    },\n  },\n);\n\nexport interface GalleryImage {\n  id: string;\n  src: string;\n  alt?: string;\n  width?: number;\n  height?: number;\n  size?: number;\n  filename?: string;\n}\n\nexport interface ImageGalleryProps\n  extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onSelect\">,\n    VariantProps<typeof galleryVariants>,\n    VariantProps<typeof imageVariants> {\n  /**\n   * Array of images to display\n   */\n  images: GalleryImage[];\n  /**\n   * Whether to enable lightbox preview\n   * @default true\n   */\n  lightbox?: boolean;\n  /**\n   * Whether to allow selecting images\n   * @default false\n   */\n  selectable?: boolean;\n  /**\n   * Currently selected image IDs\n   */\n  selected?: string[];\n  /**\n   * Called when selection changes\n   */\n  onSelect?: (selected: string[]) => void;\n  /**\n   * Called when an image is deleted\n   */\n  onDelete?: (image: GalleryImage) => void;\n  /**\n   * Called when download is clicked\n   */\n  onDownload?: (image: GalleryImage) => void;\n  /**\n   * Whether images can be deleted\n   * @default false\n   */\n  deletable?: boolean;\n  /**\n   * Whether images can be downloaded\n   * @default false\n   */\n  downloadable?: boolean;\n  /**\n   * Empty state content\n   */\n  emptyState?: React.ReactNode;\n}\n\nexport function ImageGallery({\n  images,\n  lightbox = true,\n  selectable = false,\n  selected = [],\n  onSelect,\n  onDelete,\n  onDownload,\n  deletable = false,\n  downloadable = false,\n  columns,\n  aspect,\n  emptyState,\n  className,\n  ...props\n}: ImageGalleryProps) {\n  const [lightboxOpen, setLightboxOpen] = React.useState(false);\n  const [lightboxIndex, setLightboxIndex] = React.useState(0);\n\n  const handleImageClick = React.useCallback(\n    (index: number, e: React.MouseEvent) => {\n      if (selectable) {\n        e.preventDefault();\n        const image = images[index];\n        if (!image) return;\n\n        const isSelected = selected.includes(image.id);\n        const newSelected = isSelected ? selected.filter((id) => id !== image.id) : [...selected, image.id];\n        onSelect?.(newSelected);\n      } else if (lightbox) {\n        setLightboxIndex(index);\n        setLightboxOpen(true);\n      }\n    },\n    [images, lightbox, onSelect, selectable, selected],\n  );\n\n  const handlePrevious = React.useCallback(() => {\n    setLightboxIndex((prev) => (prev > 0 ? prev - 1 : images.length - 1));\n  }, [images.length]);\n\n  const handleNext = React.useCallback(() => {\n    setLightboxIndex((prev) => (prev < images.length - 1 ? prev + 1 : 0));\n  }, [images.length]);\n\n  const handleKeyDown = React.useCallback(\n    (e: KeyboardEvent) => {\n      if (!lightboxOpen) return;\n\n      switch (e.key) {\n        case \"ArrowLeft\":\n          handlePrevious();\n          break;\n        case \"ArrowRight\":\n          handleNext();\n          break;\n        case \"Escape\":\n          setLightboxOpen(false);\n          break;\n      }\n    },\n    [lightboxOpen, handlePrevious, handleNext],\n  );\n\n  React.useEffect(() => {\n    document.addEventListener(\"keydown\", handleKeyDown);\n    return () => document.removeEventListener(\"keydown\", handleKeyDown);\n  }, [handleKeyDown]);\n\n  // Prevent body scroll when lightbox is open\n  React.useEffect(() => {\n    if (lightboxOpen) {\n      document.body.style.overflow = \"hidden\";\n    } else {\n      document.body.style.overflow = \"\";\n    }\n    return () => {\n      document.body.style.overflow = \"\";\n    };\n  }, [lightboxOpen]);\n\n  if (images.length === 0 && emptyState) {\n    return <>{emptyState}</>;\n  }\n\n  const currentImage = images[lightboxIndex];\n\n  return (\n    <>\n      <div className={cn(galleryVariants({ columns }), className)} {...props}>\n        {images.map((image, index) => {\n          const isSelected = selected.includes(image.id);\n\n          return (\n            <div\n              key={image.id}\n              role=\"button\"\n              tabIndex={0}\n              onClick={(e) => handleImageClick(index, e)}\n              onKeyDown={(e) => {\n                if (e.key === \"Enter\" || e.key === \" \") {\n                  handleImageClick(index, e as unknown as React.MouseEvent);\n                }\n              }}\n              className={cn(\n                imageVariants({ aspect }),\n                isSelected && \"ring-2 ring-primary ring-offset-2\",\n                \"focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2\",\n              )}\n            >\n              <img\n                src={image.src}\n                alt={image.alt || \"Gallery image\"}\n                className=\"h-full w-full object-cover\"\n                loading=\"lazy\"\n              />\n\n              {/* Hover overlay */}\n              <div className=\"absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-colors flex items-center justify-center\">\n                <ZoomIn className=\"h-6 w-6 text-white opacity-0 group-hover:opacity-100 transition-opacity\" />\n              </div>\n\n              {/* Selection indicator */}\n              {selectable && (\n                <div\n                  className={cn(\n                    \"absolute top-2 left-2 h-5 w-5 rounded-full border-2 transition-colors\",\n                    isSelected ? \"bg-primary border-primary\" : \"bg-white/80 border-white/80\",\n                  )}\n                >\n                  {isSelected && (\n                    <svg\n                      className=\"h-full w-full text-primary-foreground\"\n                      viewBox=\"0 0 24 24\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                      strokeWidth={3}\n                    >\n                      <polyline points=\"20 6 9 17 4 12\" />\n                    </svg>\n                  )}\n                </div>\n              )}\n\n              {/* Action buttons */}\n              {(deletable || downloadable) && (\n                <div className=\"absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity\">\n                  {downloadable && (\n                    <button\n                      type=\"button\"\n                      onClick={(e) => {\n                        e.stopPropagation();\n                        onDownload?.(image);\n                      }}\n                      className=\"h-7 w-7 rounded bg-white/90 hover:bg-white flex items-center justify-center transition-colors\"\n                    >\n                      <Download className=\"h-4 w-4\" />\n                    </button>\n                  )}\n                  {deletable && (\n                    <button\n                      type=\"button\"\n                      onClick={(e) => {\n                        e.stopPropagation();\n                        onDelete?.(image);\n                      }}\n                      className=\"h-7 w-7 rounded bg-destructive/90 hover:bg-destructive text-destructive-foreground flex items-center justify-center transition-colors\"\n                    >\n                      <Trash2 className=\"h-4 w-4\" />\n                    </button>\n                  )}\n                </div>\n              )}\n            </div>\n          );\n        })}\n      </div>\n\n      {/* Lightbox */}\n      {lightbox && lightboxOpen && currentImage && (\n        <div\n          className=\"fixed inset-0 z-50 bg-black/90 flex items-center justify-center\"\n          onClick={() => setLightboxOpen(false)}\n          onKeyDown={(e) => {\n            if (e.key === \"Escape\") {\n              setLightboxOpen(false);\n            }\n          }}\n          onKeyUp={(e) => {\n            if (e.key === \"Escape\") {\n              setLightboxOpen(false);\n            }\n          }}\n        >\n          {/* Close button */}\n          <button\n            type=\"button\"\n            onClick={() => setLightboxOpen(false)}\n            className=\"absolute top-4 right-4 h-10 w-10 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center transition-colors\"\n          >\n            <X className=\"h-5 w-5 text-white\" />\n          </button>\n\n          {/* Navigation */}\n          {images.length > 1 && (\n            <>\n              <button\n                type=\"button\"\n                onClick={(e) => {\n                  e.stopPropagation();\n                  handlePrevious();\n                }}\n                className=\"absolute left-4 h-10 w-10 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center transition-colors\"\n              >\n                <ChevronLeft className=\"h-5 w-5 text-white\" />\n              </button>\n              <button\n                type=\"button\"\n                onClick={(e) => {\n                  e.stopPropagation();\n                  handleNext();\n                }}\n                className=\"absolute right-4 h-10 w-10 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center transition-colors\"\n              >\n                <ChevronRight className=\"h-5 w-5 text-white\" />\n              </button>\n            </>\n          )}\n\n          {/* Image */}\n          <div\n            className=\"max-w-[90vw] max-h-[85vh] relative\"\n            onClick={(e) => e.stopPropagation()}\n            onKeyDown={(e) => e.stopPropagation()}\n            onKeyUp={(e) => e.stopPropagation()}\n          >\n            <img\n              src={currentImage.src}\n              alt={currentImage.alt || \"Gallery image\"}\n              className=\"max-w-full max-h-[85vh] object-contain\"\n            />\n          </div>\n\n          {/* Image info */}\n          <div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-4 text-white/80 text-sm\">\n            <span>\n              {lightboxIndex + 1} / {images.length}\n            </span>\n            {currentImage.filename && <span>{currentImage.filename}</span>}\n            {currentImage.size && <span>{formatBytes(currentImage.size)}</span>}\n            {currentImage.width && currentImage.height && (\n              <span>\n                {currentImage.width} x {currentImage.height}\n              </span>\n            )}\n\n            {/* External link */}\n            <a\n              href={currentImage.src}\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n              className=\"hover:text-white transition-colors\"\n              onClick={(e) => e.stopPropagation()}\n            >\n              <ExternalLink className=\"h-4 w-4\" />\n            </a>\n          </div>\n        </div>\n      )}\n    </>\n  );\n}\n\nImageGallery.displayName = \"ImageGallery\";\n"]}