{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-list",
  "title": "Animated List",
  "description": "A flex column that staggers items in on first mount, pops in new entries, and exits removed ones while the rest smoothly re-flow into place.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/registry/ui/animated-list.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport { cn } from \"@/lib/utils\"\n\nexport interface AnimatedListProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n  /** Each child renders as one list item — every child must carry a stable `key`. */\n  children: React.ReactNode\n  /** Stagger (ms) applied to each item's entrance delay on first mount; items added later pop in immediately with no extra delay. */\n  stagger?: number\n}\n\n/**\n * A layout-animated flex column: items stagger in on first mount, items\n * added later pop in on their own (no extra delay), removed items fade out\n * while the remaining items smoothly re-flow into place. No visual\n * opinion of its own — the animation choreography is the whole point.\n */\nexport const AnimatedList = React.forwardRef<HTMLDivElement, AnimatedListProps>(\n  ({ children, stagger = 80, className, ...props }, ref) => {\n    const reduced = useReducedMotion()\n    const mounted = React.useRef(false)\n\n    React.useEffect(() => {\n      mounted.current = true\n    }, [])\n\n    return (\n      <div className={cn(\"flex flex-col gap-2\", className)} ref={ref} {...props}>\n        <AnimatePresence initial={!reduced} mode=\"popLayout\">\n          {React.Children.map(children, (child, index) => {\n            if (!React.isValidElement(child)) return null\n            const key = child.key ?? index\n            // only the first batch (mounted still false) staggers by index; anything added later has delay 0 and pops in at once\n            const delay = mounted.current ? 0 : (index * stagger) / 1000\n\n            return (\n              <motion.div\n                animate={{ opacity: 1, y: 0, scale: 1 }}\n                exit={\n                  reduced\n                    ? { opacity: 0, transition: { duration: 0 } }\n                    : { opacity: 0, y: 12, scale: 0.97, transition: { duration: 0.2, ease: \"easeIn\" } }\n                }\n                initial={reduced ? false : { opacity: 0, y: 12, scale: 0.97 }}\n                key={key}\n                layout={!reduced}\n                transition={reduced ? { duration: 0 } : { type: \"spring\", stiffness: 300, damping: 26, delay }}\n              >\n                {child}\n              </motion.div>\n            )\n          })}\n        </AnimatePresence>\n      </div>\n    )\n  },\n)\n\nAnimatedList.displayName = \"AnimatedList\"\n\nexport default AnimatedList\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}