{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-image-gallery",
  "type": "registry:ui",
  "title": "Animated Image Gallery",
  "description": "An Animated Image Gallery component for displaying a gallery of images.",
  "files": [
    {
      "path": "src/registry/ui/animated-image-gallery.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport Image from \"next/image\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\nexport type GalleryImage = {\n  src: string;\n  /** Describes the image itself. */\n  alt: string;\n  /** Optional caption shown over the image on hover (always shown on touch devices). */\n  caption?: React.ReactNode;\n  /** Intrinsic size. When omitted the image is shown in a 4:3 frame. */\n  width?: number;\n  height?: number;\n};\n\nconst COLUMNS = {\n  2: { className: \"sm:columns-2\", sizes: \"(min-width: 640px) 50vw, 100vw\" },\n  3: { className: \"sm:columns-2 lg:columns-3\", sizes: \"(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw\" },\n  4: {\n    className: \"sm:columns-2 lg:columns-3 xl:columns-4\",\n    sizes: \"(min-width: 1280px) 25vw, (min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw\",\n  },\n} as const;\n\nconst EASE_OUT = [0.23, 1, 0.32, 1] as const;\n\nexport type ImageGalleryProps = React.ComponentProps<\"div\"> & {\n  images: GalleryImage[];\n  /** Columns at the widest breakpoint. Collapses responsively. @default 3 */\n  columns?: keyof typeof COLUMNS;\n};\n\n/** Masonry image gallery whose items fade and rise in as they scroll into view. */\nexport function ImageGallery({ images, columns = 3, className, ...props }: ImageGalleryProps) {\n  const reduceMotion = useReducedMotion();\n  const layout = COLUMNS[columns];\n\n  return (\n    <div\n      data-slot=\"animated-image-gallery\"\n      className={cn(\"columns-1 gap-4\", layout.className, className)}\n      {...props}\n    >\n      {images.map((image, index) => (\n        <motion.figure\n          key={image.src}\n          initial={reduceMotion ? false : { opacity: 0, y: 24 }}\n          whileInView={{ opacity: 1, y: 0 }}\n          viewport={{ once: true, amount: 0.2 }}\n          transition={{ duration: 0.6, ease: EASE_OUT, delay: (index % (columns * 2)) * 0.06 }}\n          className=\"group relative mb-4 break-inside-avoid overflow-hidden rounded-xl border bg-muted\"\n        >\n          {image.width && image.height ? (\n            <Image\n              src={image.src}\n              alt={image.alt}\n              width={image.width}\n              height={image.height}\n              sizes={layout.sizes}\n              className=\"h-auto w-full transition-transform duration-500 ease-out group-hover:scale-[1.02] motion-reduce:transition-none motion-reduce:group-hover:scale-100\"\n            />\n          ) : (\n            <div className=\"relative aspect-[4/3]\">\n              <Image\n                src={image.src}\n                alt={image.alt}\n                fill\n                sizes={layout.sizes}\n                className=\"object-cover transition-transform duration-500 ease-out group-hover:scale-[1.02] motion-reduce:transition-none motion-reduce:group-hover:scale-100\"\n              />\n            </div>\n          )}\n          {image.caption && (\n            <figcaption className=\"absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 to-transparent p-4 pt-10 text-sm font-medium text-white opacity-0 transition-opacity duration-300 group-hover:opacity-100 [@media(hover:none)]:opacity-100\">\n              {image.caption}\n            </figcaption>\n          )}\n        </motion.figure>\n      ))}\n    </div>\n  );\n}\n"
    }
  ],
  "dependencies": [
    "motion"
  ]
}
