{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"toast-04","type":"registry:component","title":"Loading & Progress Toast Notifications","description":"Loading and progress toast notification component showcasing different loading states and progress indicators. Features promise-based loading, incremental progress updates, simple loading states, and download progress simulation. Perfect for async operations, file uploads/downloads, and long-running processes.","dependencies":["lucide-react","sonner"],"registryDependencies":["button","card","sonner","toast-utils","toast-provider"],"files":[{"path":"src/registry/blocks/application/feedback/toasts/toast-04.tsx","content":"\"use client\";\n\nimport { CheckCircle, Download, Loader2, Upload } from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport { cn } from \"@/registry/lib/utils\";\nimport { Button } from \"@/registry/ui/button\";\nimport { Card, CardContent } from \"@/registry/ui/card\";\n\nexport interface Toast04Props {\n  className?: string;\n}\n\nconst content: {\n  header: {\n    title: string;\n    subtitle: string;\n  };\n  toasts: {\n    promise: {\n      title: string;\n      buttonText: string;\n      description: string;\n      loadingMessage: string;\n      successMessage: string;\n      errorMessage: string;\n      userData: { name: string };\n    };\n    progress: {\n      title: string;\n      buttonText: string;\n      description: string;\n      uploadMessage: string;\n      successMessage: string;\n    };\n    simpleLoading: {\n      title: string;\n      buttonText: string;\n      description: string;\n      loadingMessage: string;\n      successMessage: string;\n    };\n    download: {\n      title: string;\n      buttonText: string;\n      description: string;\n      downloadMessage: string;\n      preparingMessage: string;\n      successMessage: string;\n      successDescription: string;\n    };\n  };\n} = {\n  header: {\n    title: \"Loading & Progress Toasts\",\n    subtitle:\n      \"Toasts that show loading states, progress indicators, and async operations\",\n  },\n  toasts: {\n    promise: {\n      title: \"Promise Toast\",\n      buttonText: \"Promise Loading\",\n      description: \"Automatically handles loading, success, and error states\",\n      loadingMessage: \"Creating account...\",\n      successMessage: \"Account created for\",\n      errorMessage: \"Failed to create account\",\n      userData: { name: \"John Doe\" },\n    },\n    progress: {\n      title: \"Progress Toast\",\n      buttonText: \"Upload Progress\",\n      description: \"Shows incremental progress updates\",\n      uploadMessage: \"Uploading file...\",\n      successMessage: \"File uploaded successfully!\",\n    },\n    simpleLoading: {\n      title: \"Simple Loading\",\n      buttonText: \"Simple Loading\",\n      description: \"Basic loading toast that updates to success\",\n      loadingMessage: \"Processing request...\",\n      successMessage: \"Request completed successfully!\",\n    },\n    download: {\n      title: \"Download Progress\",\n      buttonText: \"Download Progress\",\n      description: \"Simulates file download with progress\",\n      downloadMessage: \"Downloading file...\",\n      preparingMessage: \"Preparing download...\",\n      successMessage: \"Download completed!\",\n      successDescription: \"File saved to your downloads folder\",\n    },\n  },\n};\n\nexport function Toast04({ className }: Toast04Props) {\n  const showLoadingToast = () => {\n    const promise = new Promise<{ name: string }>((resolve) => {\n      setTimeout(() => resolve(content.toasts.promise.userData), 3000);\n    });\n\n    toast.promise(promise, {\n      loading: content.toasts.promise.loadingMessage,\n      success: (data: { name: string }) =>\n        `${content.toasts.promise.successMessage} ${data.name}`,\n      error: content.toasts.promise.errorMessage,\n    });\n  };\n\n  const showProgressToast = () => {\n    let progress = 0;\n    const toastId = toast(content.toasts.progress.uploadMessage, {\n      description: \"0% complete\",\n      icon: <Loader2 className=\"h-4 w-4 animate-spin\" />,\n    });\n\n    const interval = setInterval(() => {\n      progress += 10;\n\n      if (progress <= 100) {\n        toast(content.toasts.progress.uploadMessage, {\n          description: `${progress}% complete`,\n          icon:\n            progress === 100 ? (\n              <CheckCircle className=\"h-4 w-4\" />\n            ) : (\n              <Loader2 className=\"h-4 w-4 animate-spin\" />\n            ),\n        });\n      }\n\n      if (progress >= 100) {\n        clearInterval(interval);\n        setTimeout(() => {\n          toast.success(content.toasts.progress.successMessage, {\n            id: toastId,\n          });\n        }, 500);\n      }\n    }, 300);\n  };\n\n  const showSimpleLoading = () => {\n    const loadingId = toast.loading(\n      content.toasts.simpleLoading.loadingMessage,\n    );\n\n    setTimeout(() => {\n      toast.success(content.toasts.simpleLoading.successMessage, {\n        id: loadingId,\n      });\n    }, 3000);\n  };\n\n  const showDownloadProgress = () => {\n    let progress = 0;\n    const toastId = toast(content.toasts.download.downloadMessage, {\n      description: content.toasts.download.preparingMessage,\n      icon: <Download className=\"h-4 w-4\" />,\n    });\n\n    const interval = setInterval(() => {\n      progress += Math.random() * 15 + 5; // Random progress increments\n\n      if (progress >= 100) {\n        progress = 100;\n        clearInterval(interval);\n        toast.success(content.toasts.download.successMessage, {\n          id: toastId,\n          description: content.toasts.download.successDescription,\n          icon: <CheckCircle className=\"h-4 w-4\" />,\n        });\n      } else {\n        toast(content.toasts.download.downloadMessage, {\n          description: `${Math.round(progress)}% complete`,\n          icon: <Download className=\"h-4 w-4\" />,\n        });\n      }\n    }, 400);\n  };\n\n  return (\n    <div\n      className={cn(\"w-full max-w-4xl mx-auto\", className)}\n      data-testid=\"loading-progress-toasts-container\"\n    >\n      <div className=\"text-center space-y-2 mb-8\">\n        <h1 className=\"text-3xl font-bold\">{content.header.title}</h1>\n        <p className=\"text-muted-foreground\">{content.header.subtitle}</p>\n      </div>\n\n      <Card>\n        <CardContent className=\"p-6\">\n          <div\n            className=\"grid grid-cols-1 md:grid-cols-2 gap-4\"\n            data-testid=\"loading-progress-toast-buttons-grid\"\n          >\n            <div className=\"space-y-2\" data-testid=\"promise-toast-section\">\n              <h4 className=\"font-medium\">{content.toasts.promise.title}</h4>\n              <Button\n                onClick={showLoadingToast}\n                variant=\"outline\"\n                className=\"w-full bg-transparent\"\n                data-testid=\"promise-toast-button\"\n              >\n                <Loader2 className=\"mr-2 h-4 w-4\" />\n                {content.toasts.promise.buttonText}\n              </Button>\n              <p className=\"text-sm text-muted-foreground\">\n                {content.toasts.promise.description}\n              </p>\n            </div>\n\n            <div className=\"space-y-2\" data-testid=\"progress-toast-section\">\n              <h4 className=\"font-medium\">{content.toasts.progress.title}</h4>\n              <Button\n                onClick={showProgressToast}\n                variant=\"outline\"\n                className=\"w-full bg-transparent\"\n                data-testid=\"progress-toast-button\"\n              >\n                <Upload className=\"mr-2 h-4 w-4\" />\n                {content.toasts.progress.buttonText}\n              </Button>\n              <p className=\"text-sm text-muted-foreground\">\n                {content.toasts.progress.description}\n              </p>\n            </div>\n\n            <div\n              className=\"space-y-2\"\n              data-testid=\"simple-loading-toast-section\"\n            >\n              <h4 className=\"font-medium\">\n                {content.toasts.simpleLoading.title}\n              </h4>\n              <Button\n                onClick={showSimpleLoading}\n                variant=\"outline\"\n                className=\"w-full bg-transparent\"\n                data-testid=\"simple-loading-toast-button\"\n              >\n                <Loader2 className=\"mr-2 h-4 w-4\" />\n                {content.toasts.simpleLoading.buttonText}\n              </Button>\n              <p className=\"text-sm text-muted-foreground\">\n                {content.toasts.simpleLoading.description}\n              </p>\n            </div>\n\n            <div className=\"space-y-2\" data-testid=\"download-toast-section\">\n              <h4 className=\"font-medium\">{content.toasts.download.title}</h4>\n              <Button\n                onClick={showDownloadProgress}\n                variant=\"outline\"\n                className=\"w-full bg-transparent\"\n                data-testid=\"download-toast-button\"\n              >\n                <Download className=\"mr-2 h-4 w-4\" />\n                {content.toasts.download.buttonText}\n              </Button>\n              <p className=\"text-sm text-muted-foreground\">\n                {content.toasts.download.description}\n              </p>\n            </div>\n          </div>\n        </CardContent>\n      </Card>\n    </div>\n  );\n}\n","type":"registry:component"},{"path":"src/registry/lib/utils.ts","content":"import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n","type":"registry:lib"}]}