{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "flipstack",
  "type": "registry:ui",
  "title": "FlipStack",
  "description": "A stylish and animated card stack UI for showcasing content in layered views.",
  "author": "Ahdeetai <https://aditya.is-cool.dev>",
  "registryDependencies": ["@scrollxui/card"],
  "dependencies": ["motion", "clsx", "tailwind-merge"],
  "files": [
    {
      "type": "registry:ui",
      "path": "components/ui/flipstack.tsx",
      "content": "'use client';\nimport { useState, useEffect, useRef } from 'react';\nimport { motion, AnimatePresence } from 'motion/react';\nimport { Card, CardContent } from '@/components/ui/card';\n\ninterface FlipStackCard {\n  id: number;\n  content?: React.ReactNode;\n}\n\ninterface FlipStackProps {\n  cards?: FlipStackCard[];\n  mobileDirection?: 'top' | 'bottom';\n}\n\nexport default function FlipStack({\n  cards = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }],\n  mobileDirection = 'top',\n}: FlipStackProps) {\n  const [isInView, setIsInView] = useState(false);\n  const [isMobile, setIsMobile] = useState(false);\n  const [activeIndex, setActiveIndex] = useState(0);\n  const containerRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    const checkMobile = () => setIsMobile(window.innerWidth < 1024);\n    checkMobile();\n    window.addEventListener('resize', checkMobile);\n    const observer = new IntersectionObserver(\n      ([entry]) => {\n        if (entry.isIntersecting) setIsInView(true);\n      },\n      { threshold: 0.3 },\n    );\n    const currentRef = containerRef.current;\n    if (currentRef) observer.observe(currentRef);\n    return () => {\n      window.removeEventListener('resize', checkMobile);\n      if (currentRef) observer.unobserve(currentRef);\n    };\n  }, []);\n\n  useEffect(() => {\n    if (!isMobile || !isInView) return;\n    const interval = setInterval(() => {\n      setActiveIndex((prev: number) => (prev + 1) % cards.length);\n    }, 4000);\n    return () => clearInterval(interval);\n  }, [isMobile, isInView, cards.length]);\n\n  const getRotation = (index: number) => {\n    const rotations = [-8, 5, -3, 7, -5, 4, -6, 8, -2, 3];\n    return rotations[index % rotations.length];\n  };\n\n  const isActive = (index: number) => index === activeIndex;\n\n  const getCardVariants = (index: number) => {\n    const totalCards = cards.length;\n    const centerIndex = Math.floor(totalCards / 2);\n    const positionFromCenter = index - centerIndex;\n    if (isMobile) {\n      const yInitial = mobileDirection === 'bottom' ? -100 : 100;\n      const yBounce = mobileDirection === 'bottom' ? [0, 80, 0] : [0, -80, 0];\n\n      return {\n        initial: {\n          opacity: 0,\n          scale: 0.9,\n          z: -100,\n          rotate: getRotation(index),\n          y: yInitial,\n        },\n        animate: {\n          opacity: isActive(index) ? 1 : 0.7,\n          scale: isActive(index) ? 1 : 0.95,\n          z: isActive(index) ? 0 : -100,\n          rotate: isActive(index) ? 0 : getRotation(index),\n          zIndex: isActive(index) ? 40 : totalCards + 2 - index,\n          y: isActive(index) ? yBounce : 0,\n        },\n      };\n    }\n    return {\n      initial: {\n        x: 0,\n        y: index * 8 + 100,\n        rotate: getRotation(index),\n        scale: 1,\n        zIndex: totalCards - index,\n      },\n      animate: {\n        x: positionFromCenter * 140,\n        y: Math.abs(positionFromCenter) * 30,\n        rotate: positionFromCenter * 12,\n        scale: 1,\n        zIndex: totalCards - Math.abs(positionFromCenter),\n      },\n    };\n  };\n\n  return (\n    <div className='h-full w-full py-2'>\n      <div className='max-w-7xl mx-auto px-4 sm:px-6 lg:px-8'>\n        <div className='flex justify-center items-center'>\n          <div\n            ref={containerRef}\n            className='relative h-96 w-full max-w-md mx-auto'\n          >\n            {isMobile ? (\n              <div className='relative h-full w-full'>\n                <AnimatePresence>\n                  {cards.map((card, index: number) => {\n                    const variants = getCardVariants(index);\n                    return (\n                      <motion.div\n                        key={card.id}\n                        className='absolute inset-0 origin-bottom'\n                        initial='initial'\n                        animate={isInView ? 'animate' : 'initial'}\n                        exit={{\n                          opacity: 0,\n                          scale: 0.9,\n                          z: 100,\n                          rotate: getRotation(index),\n                        }}\n                        variants={variants}\n                        transition={{ duration: 0.4, ease: 'easeInOut' }}\n                      >\n                        <Card className='w-full h-full shadow-2xl border-0 bg-white dark:bg-gray-800 overflow-hidden'>\n                          <CardContent className='p-0 h-full flex items-center justify-center'>\n                            {card.content}\n                          </CardContent>\n                        </Card>\n                      </motion.div>\n                    );\n                  })}\n                </AnimatePresence>\n              </div>\n            ) : (\n              <div\n                className='relative h-full w-full flex items-center justify-center'\n                style={{ perspective: '1000px' }}\n              >\n                {cards.map((card, index: number) => {\n                  const variants = getCardVariants(index);\n                  return (\n                    <motion.div\n                      key={card.id}\n                      className='absolute origin-bottom'\n                      initial='initial'\n                      animate={isInView ? 'animate' : 'initial'}\n                      variants={variants}\n                      transition={{\n                        duration: 0.8,\n                        delay: index * 0.1,\n                        ease: 'easeOut',\n                      }}\n                    >\n                      <Card className='w-80 h-96 shadow-2xl border-0 bg-white dark:bg-gray-800 overflow-hidden'>\n                        <CardContent className='p-0 h-full flex items-center justify-center'>\n                          {card.content}\n                        </CardContent>\n                      </Card>\n                    </motion.div>\n                  );\n                })}\n              </div>\n            )}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
    },
    {
      "type": "registry:ui",
      "path": "components/ui/card.tsx",
      "content": "import * as React from 'react';\n\nimport { cn } from '@/lib/utils';\n\nfunction Card({ className, ...props }: React.ComponentProps<'div'>) {\n  return (\n    <div\n      data-slot='card'\n      className={cn(\n        'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-xs',\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction CardHeader({ className, ...props }: React.ComponentProps<'div'>) {\n  return (\n    <div\n      data-slot='card-header'\n      className={cn(\n        '@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction CardTitle({ className, ...props }: React.ComponentProps<'div'>) {\n  return (\n    <div\n      data-slot='card-title'\n      className={cn('leading-none font-semibold', className)}\n      {...props}\n    />\n  );\n}\n\nfunction CardDescription({ className, ...props }: React.ComponentProps<'div'>) {\n  return (\n    <div\n      data-slot='card-description'\n      className={cn('text-muted-foreground text-sm', className)}\n      {...props}\n    />\n  );\n}\n\nfunction CardAction({ className, ...props }: React.ComponentProps<'div'>) {\n  return (\n    <div\n      data-slot='card-action'\n      className={cn(\n        'col-start-2 row-span-2 row-start-1 self-start justify-self-end',\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction CardContent({ className, ...props }: React.ComponentProps<'div'>) {\n  return (\n    <div\n      data-slot='card-content'\n      className={cn('px-6', className)}\n      {...props}\n    />\n  );\n}\n\nfunction CardFooter({ className, ...props }: React.ComponentProps<'div'>) {\n  return (\n    <div\n      data-slot='card-footer'\n      className={cn('flex items-center px-6 [.border-t]:pt-6', className)}\n      {...props}\n    />\n  );\n}\n\nexport {\n  Card,\n  CardHeader,\n  CardFooter,\n  CardTitle,\n  CardAction,\n  CardDescription,\n  CardContent,\n};\n"
    },
    {
      "type": "registry:lib",
      "path": "lib/utils.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}"
    }
  ]
}
