{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dropzone",
  "type": "registry:block",
  "title": "Dropzone",
  "description": "A file dropzone component.",
  "dependencies": [
    "react-dropzone",
    "lucide-react"
  ],
  "files": [
    {
      "path": "examples/components/src/components/upload/dropzone.tsx",
      "content": "'use client';\n\nimport { cn } from '@/lib/utils';\nimport { AlertCircleIcon, UploadCloudIcon } from 'lucide-react';\nimport * as React from 'react';\nimport { useDropzone, type DropzoneOptions } from 'react-dropzone';\nimport { formatFileSize, useUploader } from './uploader-provider';\n\nconst DROPZONE_VARIANTS = {\n  base: 'relative rounded-md p-4 w-full flex justify-center items-center flex-col cursor-pointer border-2 border-dashed border-muted-foreground transition-colors duration-200 ease-in-out',\n  active: 'border-primary',\n  disabled:\n    'bg-muted border-muted-foreground cursor-default pointer-events-none opacity-50',\n  accept: 'border-primary bg-primary/10',\n  reject: 'border-destructive bg-destructive/10',\n};\n\n/**\n * Props for the Dropzone component.\n *\n * @interface DropzoneProps\n * @extends {React.HTMLAttributes<HTMLInputElement>}\n */\nexport interface DropzoneProps extends React.HTMLAttributes<HTMLInputElement> {\n  /**\n   * Options passed to the underlying react-dropzone component.\n   * Cannot include 'disabled' or 'onDrop' as they are handled internally.\n   */\n  dropzoneOptions?: Omit<DropzoneOptions, 'disabled' | 'onDrop'>;\n\n  /**\n   * Whether the dropzone is disabled.\n   */\n  disabled?: boolean;\n\n  /**\n   * Message shown when files are being dragged over the dropzone.\n   */\n  dropMessageActive?: string;\n\n  /**\n   * Default message shown when the dropzone is idle.\n   */\n  dropMessageDefault?: string;\n}\n\n/**\n * A dropzone component for file uploads that integrates with the UploaderProvider.\n *\n * @component\n * @example\n * ```tsx\n * <Dropzone\n *   dropzoneOptions={{\n *     maxFiles: 5,\n *     maxSize: 1024 * 1024 * 10, // 10MB\n *   }}\n * />\n * ```\n */\nconst Dropzone = React.forwardRef<HTMLInputElement, DropzoneProps>(\n  (\n    {\n      dropzoneOptions,\n      className,\n      disabled,\n      dropMessageActive = 'Drop files here...',\n      dropMessageDefault = 'drag & drop files here, or click to select',\n      ...props\n    },\n    ref,\n  ) => {\n    const { fileStates, addFiles } = useUploader();\n    const [error, setError] = React.useState<string>();\n\n    const maxFiles = dropzoneOptions?.maxFiles;\n    const maxSize = dropzoneOptions?.maxSize;\n    const isMaxFilesReached = !!maxFiles && fileStates.length >= maxFiles;\n    const isDisabled = disabled ?? isMaxFilesReached;\n\n    const {\n      getRootProps,\n      getInputProps,\n      isDragActive,\n      isFocused,\n      isDragAccept,\n      isDragReject,\n    } = useDropzone({\n      disabled: isDisabled,\n      onDrop: (acceptedFiles, rejectedFiles) => {\n        setError(undefined);\n\n        // Handle rejections first\n        if (rejectedFiles.length > 0) {\n          if (rejectedFiles[0]?.errors[0]) {\n            const error = rejectedFiles[0].errors[0];\n            const code = error.code;\n            const messages: Record<string, string> = {\n              'file-too-large': `The file is too large. Max size is ${formatFileSize(\n                maxSize ?? 0,\n              )}.`,\n              'file-invalid-type': 'Invalid file type.',\n              'too-many-files': `You can only add ${\n                maxFiles ?? 'multiple'\n              } file(s).`,\n              default: 'The file is not supported.',\n            };\n            setError(messages[code] ?? messages.default);\n          }\n          return; // Exit early if there are any rejections\n        }\n\n        // Handle accepted files\n        if (acceptedFiles.length === 0) return;\n\n        // Check if adding these files would exceed maxFiles limit\n        if (maxFiles) {\n          const remainingSlots = maxFiles - fileStates.length;\n          // If adding all files would exceed the limit, reject them all\n          if (acceptedFiles.length > remainingSlots) {\n            setError(`You can only add ${maxFiles} file(s).`);\n            return;\n          }\n        }\n\n        addFiles(acceptedFiles);\n      },\n      ...dropzoneOptions,\n    });\n\n    const dropZoneClassName = React.useMemo(\n      () =>\n        cn(\n          DROPZONE_VARIANTS.base,\n          isFocused && DROPZONE_VARIANTS.active,\n          isDisabled && DROPZONE_VARIANTS.disabled,\n          isDragReject && DROPZONE_VARIANTS.reject,\n          isDragAccept && DROPZONE_VARIANTS.accept,\n          className,\n        ),\n      [isFocused, isDisabled, isDragAccept, isDragReject, className],\n    );\n\n    return (\n      <div className=\"w-full\">\n        <div\n          {...getRootProps({\n            className: dropZoneClassName,\n          })}\n        >\n          <input ref={ref} {...getInputProps()} {...props} />\n          <div className=\"flex flex-col items-center justify-center gap-2 text-center text-muted-foreground\">\n            <UploadCloudIcon className=\"h-10 w-10\" />\n            <div className=\"text-sm font-medium\">\n              {isDragActive ? dropMessageActive : dropMessageDefault}\n            </div>\n            {(!!maxSize || !!maxFiles) && (\n              <div className=\"text-xs\">\n                {maxFiles && maxFiles > 1 ? `Up to ${maxFiles} files` : ''}\n                {maxFiles && maxFiles > 1 && maxSize ? ', ' : ''}\n                {maxSize && `Max size: ${formatFileSize(maxSize)}`}\n              </div>\n            )}\n          </div>\n        </div>\n\n        {/* Error Text */}\n        {error && (\n          <div className=\"mt-1 flex items-center text-xs text-destructive\">\n            <AlertCircleIcon className=\"mr-1 h-4 w-4\" />\n            <span>{error}</span>\n          </div>\n        )}\n      </div>\n    );\n  },\n);\nDropzone.displayName = 'Dropzone';\n\nexport { Dropzone };\n",
      "type": "registry:component",
      "target": "components/upload/dropzone.tsx"
    }
  ]
}