{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "transitions-shared-modal",
  "type": "registry:component",
  "title": "Shared Modal",
  "description": "A trigger that morphs into a modal with multiple animation styles.",
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/new-york/animations/transitions/shared-modal.tsx",
      "content": "\"use client\";\n\nimport React, { useState, useId } from \"react\";\nimport { motion, AnimatePresence } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\nimport { Plus } from \"lucide-react\";\n\ninterface SharedModalProps {\n    variant?: \"scale\" | \"slide-bottom\" | \"slide-right\" | \"fade-in\" | \"scale-up\";\n}\n\nexport function SharedModal({ variant = \"scale\" }: SharedModalProps = {}) {\n    const [isOpen, setIsOpen] = useState(false);\n    const id = useId();\n\n    const getModalTransition = () => {\n        switch (variant) {\n            case \"scale\":\n                return { type: \"spring\" as const, bounce: 0.2, duration: 0.6 };\n            case \"slide-bottom\":\n                return { type: \"spring\" as const, bounce: 0.15, duration: 0.5 };\n            case \"slide-right\":\n                return { type: \"spring\" as const, bounce: 0.15, duration: 0.5 };\n            case \"fade-in\":\n                return { type: \"tween\" as const, duration: 0.3, ease: \"easeOut\" as const };\n            case \"scale-up\":\n                return { type: \"spring\" as const, bounce: 0.5, duration: 0.8 };\n            default:\n                return { type: \"spring\" as const, bounce: 0.2, duration: 0.6 };\n        }\n    };\n\n    const getModalInitial = () => {\n        switch (variant) {\n            case \"slide-bottom\":\n                return { opacity: 0, y: \"100%\" };\n            case \"slide-right\":\n                return { opacity: 0, x: \"100%\" };\n            case \"fade-in\":\n                return { opacity: 0, scale: 1.1 };\n            case \"scale-up\":\n                return { opacity: 0, scale: 0.5 };\n            default:\n                return {};\n        }\n    };\n\n    const getModalAnimate = () => {\n        switch (variant) {\n            case \"slide-bottom\":\n                return { opacity: 1, y: 0 };\n            case \"slide-right\":\n                return { opacity: 1, x: 0 };\n            case \"fade-in\":\n                return { opacity: 1, scale: 1 };\n            case \"scale-up\":\n                return { opacity: 1, scale: 1 };\n            default:\n                return {};\n        }\n    };\n\n    return (\n        <div className=\"flex items-center justify-center p-8 bg-zinc-50 dark:bg-zinc-900 rounded-xl min-h-[400px] w-full\">\n            <AnimatePresence>\n                {!isOpen ? (\n                    <motion.div\n                        layoutId={`${id}-modal`}\n                        onClick={() => setIsOpen(true)}\n                        className=\"h-16 w-16 bg-white dark:bg-zinc-800 rounded-full shadow-lg cursor-pointer flex items-center justify-center border border-zinc-200 dark:border-zinc-700 hover:scale-105 transition-transform\"\n                        whileHover={{ scale: 1.05 }}\n                        whileTap={{ scale: 0.95 }}\n                    >\n                        <motion.div layoutId={`${id}-icon`}>\n                            <Plus className=\"text-zinc-500 dark:text-zinc-400\" />\n                        </motion.div>\n                    </motion.div>\n                ) : (\n                    <div className={cn(\n                        \"fixed inset-0 z-[100] px-4 pointer-events-none\",\n                        (variant === \"slide-bottom\" || variant === \"slide-right\") ? \"flex\" : \"grid place-items-center\",\n                        variant === \"slide-bottom\" && \"items-end justify-center pb-4\",\n                        variant === \"slide-right\" && \"items-center justify-end pr-4\"\n                    )}>\n                        <motion.div\n                            initial={{ opacity: 0 }}\n                            animate={{ opacity: 1 }}\n                            exit={{ opacity: 0 }}\n                            onClick={() => setIsOpen(false)}\n                            className=\"absolute inset-0 bg-black/20 backdrop-blur-sm pointer-events-auto\"\n                        />\n                        <motion.div\n                            {...(variant === \"scale\" ? { layoutId: `${id}-modal` } : {})}\n                            className=\"w-full max-w-sm bg-white dark:bg-zinc-800 rounded-2xl shadow-2xl overflow-hidden z-20 relative pointer-events-auto\"\n                            transition={getModalTransition()}\n                            initial={getModalInitial()}\n                            animate={getModalAnimate()}\n                            exit={getModalInitial()}\n                            style={{ transformPerspective: 1000 }}\n                        >\n                            <motion.div layoutId={`${id}-icon`} className=\"absolute top-4 right-4 z-10\">\n                                <button\n                                    onClick={(e) => { e.stopPropagation(); setIsOpen(false); }}\n                                    className=\"p-1 rounded-full bg-black/10 dark:bg-white/10 hover:bg-black/20 dark:hover:bg-white/20 transition-colors\"\n                                >\n                                    <Plus className=\"rotate-45 text-zinc-500 dark:text-zinc-400\" />\n                                </button>\n                            </motion.div>\n\n                            <img\n                                src=\"https://picsum.photos/seed/modal1/400/200\"\n                                alt=\"Project header\"\n                                className=\"h-32 w-full object-cover\"\n                            />\n                            <div className=\"p-6\">\n                                <h3 className=\"text-xl font-bold text-zinc-900 dark:text-zinc-100 mb-2\">New Project</h3>\n                                <p className=\"text-zinc-500 dark:text-zinc-400 mb-6\">Create a new project to get started with your ideas.</p>\n                                <div className=\"flex justify-end gap-2\">\n                                    <button\n                                        onClick={(e) => {\n                                            e.stopPropagation();\n                                            setIsOpen(false);\n                                        }}\n                                        className=\"px-4 py-2 text-sm font-medium text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700 rounded-lg transition-colors\"\n                                    >\n                                        Cancel\n                                    </button>\n                                    <button\n                                        onClick={(e) => {\n                                            e.stopPropagation();\n                                            alert(\"Created!\");\n                                            setIsOpen(false);\n                                        }}\n                                        className=\"px-4 py-2 text-sm font-medium bg-zinc-900 dark:bg-zinc-100 text-white dark:text-zinc-900 rounded-lg hover:opacity-90 transition-opacity\"\n                                    >\n                                        Create\n                                    </button>\n                                </div>\n                            </div>\n                        </motion.div>\n                    </div>\n                )}\n            </AnimatePresence>\n        </div>\n    );\n}\n",
      "type": "registry:component"
    }
  ]
}