{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"morphy-button","type":"registry:ui","title":"Morphy Button","description":"A pill that morphs into a spinning circle while loading, then pops a drawn check on success.","dependencies":["motion"],"registryDependencies":[],"files":[{"path":"registry/ui/morphy-button.tsx","type":"registry:ui","content":"\"use client\";\n\nimport type { ButtonHTMLAttributes } from \"react\";\nimport { AnimatePresence, MotionConfig, motion } from \"motion/react\";\n\nexport type MorphyStatus = \"idle\" | \"loading\" | \"success\";\n\nexport interface MorphyButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n  /** Visual state: pill label, spinner circle, or check. */\n  status?: MorphyStatus;\n  /** Fill color in the success state. */\n  successColor?: string;\n}\n\nconst spring = { type: \"spring\", stiffness: 420, damping: 32 } as const;\nconst swap = {\n  initial: { opacity: 0, scale: 0.5, filter: \"blur(4px)\" },\n  animate: { opacity: 1, scale: 1, filter: \"blur(0px)\" },\n  exit: { opacity: 0, scale: 0.5, filter: \"blur(4px)\" },\n  transition: { duration: 0.22 },\n};\n\nexport function MorphyButton({\n  status = \"idle\",\n  successColor = \"#22c55e\",\n  className,\n  children,\n  ...props\n}: MorphyButtonProps) {\n  const idle = status === \"idle\";\n  const success = status === \"success\";\n\n  return (\n    <MotionConfig reducedMotion=\"user\">\n      <button\n        type=\"button\"\n        aria-busy={status === \"loading\"}\n        {...props}\n        className={`group inline-flex rounded-full focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-ring disabled:cursor-not-allowed disabled:opacity-50 ${className ?? \"\"}`}\n      >\n        <motion.span\n          initial={false}\n          animate={{ width: idle ? \"auto\" : 48, scale: success ? [1, 1.12, 1] : 1 }}\n          transition={{ width: spring, scale: { duration: 0.45, ease: \"easeOut\" } }}\n          className={`relative flex h-12 items-center justify-center overflow-hidden rounded-full bg-primary text-sm font-semibold text-primary-foreground shadow-[inset_0_1px_0_rgb(255_255_255/.4),0_8px_24px_-12px_var(--primary)] transition-[background-color,filter] duration-300 group-enabled:group-hover:brightness-110`}\n          style={success ? { backgroundColor: successColor, color: \"#fff\" } : undefined}\n        >\n          <AnimatePresence mode=\"popLayout\" initial={false}>\n            {idle && (\n              <motion.span key=\"label\" {...swap} className=\"flex items-center gap-2 whitespace-nowrap px-8\">\n                {children}\n              </motion.span>\n            )}\n            {status === \"loading\" && (\n              <motion.span key=\"spin\" {...swap} className=\"grid place-items-center\">\n                <svg viewBox=\"0 0 24 24\" fill=\"none\" className=\"size-5 animate-spin\" aria-hidden>\n                  <circle cx=\"12\" cy=\"12\" r=\"9\" stroke=\"currentColor\" strokeOpacity=\".25\" strokeWidth=\"3\" />\n                  <path d=\"M12 3a9 9 0 0 1 9 9\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" />\n                </svg>\n              </motion.span>\n            )}\n            {success && (\n              <motion.span key=\"check\" {...swap} className=\"grid place-items-center\">\n                <svg viewBox=\"0 0 24 24\" fill=\"none\" className=\"size-6\" aria-hidden>\n                  <motion.path\n                    d=\"M5 12.5l4.5 4.5L19 7.5\"\n                    stroke=\"currentColor\"\n                    strokeWidth=\"2.75\"\n                    strokeLinecap=\"round\"\n                    strokeLinejoin=\"round\"\n                    initial={{ pathLength: 0 }}\n                    animate={{ pathLength: 1 }}\n                    transition={{ duration: 0.35, delay: 0.12, ease: \"easeOut\" }}\n                  />\n                </svg>\n              </motion.span>\n            )}\n          </AnimatePresence>\n        </motion.span>\n        <span className=\"sr-only\" aria-live=\"polite\">\n          {status === \"loading\" ? \"Loading\" : success ? \"Done\" : \"\"}\n        </span>\n      </button>\n    </MotionConfig>\n  );\n}\n"}]}