{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "async-button",
  "type": "registry:component",
  "description": "A button that performs async operations with a no-layout-shift loading state",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "button",
    "https://kaui-shadcn-registry.vercel.app/r/use-async.json"
  ],
  "files": [
    {
      "type": "registry:component",
      "target": "components/ui/async-button.tsx",
      "path": "src/registry/base/async-button/components/async-button.tsx",
      "content": "import { Button } from \"@/components/ui/button\";\nimport {\n  useAsync,\n  type UseAsyncOptions,\n} from \"@/hooks/use-async\";\nimport { type ComponentProps, type ReactNode } from \"react\";\nimport { Loader2 } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport type AsyncButtonProps<\n  TData,\n  TError,\n  TArgs extends unknown[] = [],\n> = Omit<ComponentProps<typeof Button>, \"onClick\"> &\n  UseAsyncOptions<TData, TError, TArgs> & {\n    /** Content displayed while the action is in flight. Defaults to `children` when omitted. */\n    loadingText?: ReactNode;\n  };\n\n/**\n * A `Button` that manages its own async state.\n *\n * Disables itself while the action is in flight and swaps children for a spinner\n * (+ optional `loadingText`) via an invisible overlay — the button width never shifts.\n *\n * **Required:** `action`\n */\nexport function AsyncButton<TData, TError, TArgs extends unknown[] = []>({\n  action,\n  loadingText,\n  onSuccess,\n  onError,\n  onSettled,\n  children,\n  disabled,\n  className,\n  ...props\n}: AsyncButtonProps<TData, TError, TArgs>) {\n  const { isLoading, execute } = useAsync({\n    action,\n    onSuccess,\n    onError,\n    onSettled,\n  });\n\n  const resolvedLoadingText = loadingText ?? children;\n\n  return (\n    <Button\n      {...props}\n      disabled={disabled || isLoading}\n      onClick={() => {\n        (execute as () => Promise<TData>)();\n      }}\n      className={cn(\"grid grid-cols-1 place-items-center\", className)}\n    >\n      <div\n        className={cn(\n          \"col-start-1 row-start-1 flex items-center justify-center gap-2 transition-all\",\n          isLoading ? \"invisible opacity-0\" : \"visible opacity-100\",\n        )}\n      >\n        {children}\n      </div>\n\n      <div\n        className={cn(\n          \"col-start-1 row-start-1 flex items-center justify-center gap-2\",\n          isLoading ? \"visible opacity-100\" : \"invisible opacity-0\",\n        )}\n      >\n        <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n        {resolvedLoadingText && <span>{resolvedLoadingText}</span>}\n      </div>\n    </Button>\n  );\n}\n"
    }
  ]
}