{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "p-toast-9",
  "description": "Long-running promise toast with cancel",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "@coss/button",
    "@coss/toast"
  ],
  "files": [
    {
      "path": "registry/default/particles/p-toast-9.tsx",
      "content": "\"use client\";\n\nimport { DownloadIcon } from \"lucide-react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { Button } from \"@/registry/default/ui/button\";\nimport { toastManager } from \"@/registry/default/ui/toast\";\n\nexport default function Particle() {\n  const [isGenerating, setIsGenerating] = useState(false);\n  const [progress, setProgress] = useState(0);\n  const abortControllerRef = useRef<AbortController | null>(null);\n\n  useEffect(() => {\n    if (!isGenerating) return;\n\n    const interval = setInterval(() => {\n      setProgress((prev) =>\n        Math.min(99, prev + Math.round(Math.random() * 8 + 2)),\n      );\n    }, 300);\n\n    return () => clearInterval(interval);\n  }, [isGenerating]);\n\n  async function handleDownload() {\n    if (isGenerating) return;\n\n    setIsGenerating(true);\n    setProgress(0);\n    abortControllerRef.current = new AbortController();\n\n    try {\n      await toastManager.promise(\n        new Promise<string>((resolve, reject) => {\n          const shouldSucceed = Math.random() > 0.2;\n          const timeoutId = setTimeout(() => {\n            if (shouldSucceed) {\n              resolve(\"Report ready\");\n            } else {\n              reject(new Error(\"Generation failed\"));\n            }\n          }, 4000);\n\n          abortControllerRef.current?.signal.addEventListener(\"abort\", () => {\n            clearTimeout(timeoutId);\n            reject(new DOMException(\"Cancelled\", \"AbortError\"));\n          });\n        }),\n        {\n          error: (err: Error) => {\n            if (err.name === \"AbortError\") {\n              return {\n                actionProps: undefined,\n                description: \"Report generation was cancelled.\",\n                title: \"Cancelled\",\n                type: \"info\" as const,\n              };\n            }\n            return {\n              actionProps: undefined,\n              description: \"Please try again later.\",\n              title: \"Failed to generate report\",\n            };\n          },\n          loading: {\n            actionProps: {\n              children: \"Cancel\",\n              onClick: () => abortControllerRef.current?.abort(),\n            },\n            description: \"Your download will begin once ready.\",\n            title: \"Generating report…\",\n          },\n          success: () => ({\n            actionProps: undefined,\n            description: \"Your file is now downloading.\",\n            title: \"Download started\",\n          }),\n        },\n      );\n    } finally {\n      setIsGenerating(false);\n      setProgress(0);\n      abortControllerRef.current = null;\n    }\n  }\n\n  return (\n    <Button disabled={isGenerating} onClick={handleDownload} variant=\"outline\">\n      {isGenerating ? (\n        <>\n          Loading…{\" \"}\n          <span className=\"tabular-nums\">\n            {progress.toString().padStart(2, \"\\u2007\")}%\n          </span>\n        </>\n      ) : (\n        <>\n          <DownloadIcon />\n          Download\n        </>\n      )}\n    </Button>\n  );\n}\n",
      "type": "registry:block"
    }
  ],
  "categories": [
    "toast",
    "button"
  ],
  "type": "registry:block"
}