{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-buttons",
  "title": "Animated Buttons",
  "description": "Animated action buttons that transition through hover, loading, and complete states.",
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "button"
  ],
  "files": [
    {
      "path": "registry/corr/components/animated-buttons.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport { Check, X } from \"lucide-react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { cn } from \"@/lib/utils\"\n\ntype AnimatedButtonStatus = \"idle\" | \"loading\" | \"success\"\n\ntype AnimatedButtonProps = {\n  label?: string\n  loadingLabel?: string\n  successLabel?: string\n  icon?: React.ReactNode\n  hoverIcon?: React.ReactNode\n  iconOnlyOnHover?: boolean\n  successIcon?: React.ReactNode\n  tone?: \"default\" | \"destructive\"\n  onAction?: () => void | Promise<void>\n  disabled?: boolean\n  resetDelay?: number\n  className?: string\n  variant?: \"default\" | \"outline\"\n  minWidth?: string\n}\n\nexport function AnimatedButton({\n  label = \"Confirm\",\n  loadingLabel = \"Working\",\n  successLabel = \"Done\",\n  icon,\n  hoverIcon,\n  iconOnlyOnHover = false,\n  successIcon,\n  tone = \"default\",\n  onAction,\n  disabled = false,\n  resetDelay = 1100,\n  className,\n  variant,\n  minWidth = \"min-w-28\",\n}: AnimatedButtonProps) {\n  const reduceMotion = useReducedMotion()\n  const resetTimerRef = React.useRef<number | null>(null)\n  const [status, setStatus] = React.useState<AnimatedButtonStatus>(\"idle\")\n\n  React.useEffect(() => {\n    return () => {\n      if (resetTimerRef.current) {\n        window.clearTimeout(resetTimerRef.current)\n      }\n    }\n  }, [])\n\n  async function handleClick() {\n    if (disabled || status !== \"idle\") return\n\n    setStatus(\"loading\")\n\n    if (onAction) {\n      await onAction()\n    } else {\n      await new Promise((resolve) => window.setTimeout(resolve, 850))\n    }\n\n    setStatus(\"success\")\n    resetTimerRef.current = window.setTimeout(\n      () => {\n        setStatus(\"idle\")\n      },\n      reduceMotion ? 0 : resetDelay\n    )\n  }\n\n  const contentTransition = reduceMotion\n    ? { duration: 0 }\n    : { duration: 0.16, ease: \"easeOut\" as const }\n  const currentIcon =\n    status === \"idle\"\n      ? icon\n      : status === \"loading\"\n        ? \"loading\"\n        : (successIcon ?? <Check className=\"size-3.5\" />)\n  const showIconSlot = Boolean(currentIcon)\n  const effectiveVariant =\n    variant ?? (tone === \"destructive\" ? \"destructive\" : \"default\")\n\n  return (\n    <Button\n      type=\"button\"\n      variant={effectiveVariant}\n      disabled={disabled || status !== \"idle\"}\n      onClick={handleClick}\n      className={cn(\n        \"group/animated-button relative overflow-hidden transition-colors\",\n        minWidth,\n        status === \"success\" &&\n          tone !== \"destructive\" &&\n          \"bg-emerald-600 text-white hover:bg-emerald-600\",\n        className\n      )}\n    >\n      {status === \"idle\" && hoverIcon && !icon ? (\n        <>\n          <span className=\"transition-all duration-150 group-hover/animated-button:translate-y-1 group-hover/animated-button:opacity-0 group-hover/animated-button:blur-xs\">\n            {label}\n          </span>\n          <span className=\"absolute inline-flex size-3.5 translate-y-2 items-center justify-center opacity-0 transition-all duration-150 group-hover/animated-button:translate-y-0 group-hover/animated-button:opacity-100\">\n            {hoverIcon}\n          </span>\n        </>\n      ) : status === \"idle\" && icon && iconOnlyOnHover ? (\n        <>\n          <span className=\"inline-flex items-center gap-1.5 transition-all duration-150 group-hover/animated-button:translate-y-1 group-hover/animated-button:opacity-0 group-hover/animated-button:blur-xs\">\n            <span className=\"inline-flex size-3.5 shrink-0 items-center justify-center\">\n              {icon}\n            </span>\n            {label}\n          </span>\n          <span className=\"absolute inline-flex size-3.5 translate-y-2 items-center justify-center opacity-0 transition-all duration-150 group-hover/animated-button:translate-y-0 group-hover/animated-button:opacity-100\">\n            {icon}\n          </span>\n        </>\n      ) : (\n        <AnimatePresence mode=\"wait\" initial={false}>\n          <motion.span\n            key={status}\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            exit={{ opacity: 0 }}\n            transition={contentTransition}\n            className={cn(\n              \"inline-flex items-center\",\n              showIconSlot && \"gap-1.5\"\n            )}\n          >\n            {showIconSlot ? (\n              <span className=\"inline-flex size-3.5 shrink-0 items-center justify-center\">\n                {currentIcon === \"loading\" ? (\n                  <span\n                    className={cn(\n                      \"inline-flex size-3.5 items-center justify-center rounded-full border-2 border-current border-r-transparent\",\n                      !reduceMotion && \"animate-spin\"\n                    )}\n                    aria-hidden=\"true\"\n                  />\n                ) : (\n                  currentIcon\n                )}\n              </span>\n            ) : null}\n            {status === \"idle\"\n              ? label\n              : status === \"loading\"\n                ? loadingLabel\n                : successLabel}\n          </motion.span>\n        </AnimatePresence>\n      )}\n    </Button>\n  )\n}\n\nexport function AnimatedButtons({\n  confirmLabel = \"Confirm\",\n  cancelLabel = \"Cancel\",\n  loadingLabel = \"Working\",\n  successLabel = \"Done\",\n  confirmIcon,\n  successIcon,\n  tone = \"default\",\n  onAction,\n  onCancel,\n  disabled = false,\n  resetDelay = 1100,\n  className,\n}: {\n  confirmLabel?: string\n  cancelLabel?: string\n  loadingLabel?: string\n  successLabel?: string\n  confirmIcon?: React.ReactNode\n  successIcon?: React.ReactNode\n  tone?: \"default\" | \"destructive\"\n  onAction?: () => void | Promise<void>\n  onCancel?: () => void\n  disabled?: boolean\n  resetDelay?: number\n  className?: string\n}) {\n  return (\n    <div\n      className={cn(\n        \"inline-flex items-center gap-2 rounded-md border bg-card p-1.5 shadow-sm\",\n        className\n      )}\n    >\n      <Button\n        type=\"button\"\n        variant=\"outline\"\n        disabled={disabled}\n        onClick={onCancel}\n        className=\"group/cancel relative min-w-24 overflow-hidden\"\n      >\n        <span className=\"transition-all duration-150 group-hover/cancel:translate-y-1 group-hover/cancel:opacity-0 group-hover/cancel:blur-xs\">\n          {cancelLabel}\n        </span>\n        <X className=\"absolute size-3.5 translate-y-2 opacity-0 transition-all duration-150 group-hover/cancel:translate-y-0 group-hover/cancel:opacity-100\" />\n      </Button>\n\n      <AnimatedButton\n        label={confirmLabel}\n        loadingLabel={loadingLabel}\n        successLabel={successLabel}\n        icon={confirmIcon}\n        hoverIcon={!confirmIcon ? <Check className=\"size-3.5\" /> : undefined}\n        successIcon={successIcon}\n        tone={tone}\n        onAction={onAction}\n        disabled={disabled}\n        resetDelay={resetDelay}\n      />\n    </div>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}