UNPKG

analytica-frontend-lib

Version:

Repositório público dos componentes utilizados nas plataformas da Analytica Ensino

1 lines 9.03 kB
{"version":3,"sources":["../src/components/ImageUpload/ImageUpload.tsx"],"sourcesContent":["import type React from 'react';\nimport { useRef, useState } from 'react';\nimport { ImageIcon } from '@phosphor-icons/react/dist/csr/Image';\nimport { PaperclipIcon } from '@phosphor-icons/react/dist/csr/Paperclip';\nimport { XIcon } from '@phosphor-icons/react/dist/csr/X';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport { cn } from '../../utils/utils';\n\nexport interface ImageUploadProps {\n /** File currently selected */\n selectedFile?: File | null;\n /** Upload progress (0-100). If not provided, progress bar won't show */\n uploadProgress?: number;\n /** Show progress bar even if uploadProgress is 100 */\n showProgress?: boolean;\n /** Callback when a file is selected */\n onFileSelect?: (file: File) => void;\n /** Callback when file is removed */\n onRemoveFile?: () => void;\n /** Custom button text when no file is selected */\n buttonText?: string;\n /** Custom button icon when no file is selected */\n buttonIcon?: React.ReactNode;\n /** Accept specific file types (default: \"image/*\") */\n accept?: string;\n /** Disabled state */\n disabled?: boolean;\n /** Custom class name */\n className?: string;\n /** Max file size in bytes */\n maxSize?: number;\n /** Callback when file size exceeds maxSize */\n onSizeError?: (file: File, maxSize: number) => void;\n /** Callback when invalid file type is selected */\n onTypeError?: (file: File) => void;\n /** Variant of the upload button */\n variant?: 'default' | 'compact';\n}\n\nexport default function ImageUpload({\n selectedFile,\n onFileSelect,\n onRemoveFile,\n buttonText = 'Inserir imagem',\n buttonIcon,\n accept = 'image/*',\n disabled = false,\n className,\n maxSize,\n onSizeError,\n onTypeError,\n variant = 'default',\n}: Readonly<ImageUploadProps>) {\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [internalFile, setInternalFile] = useState<File | null>(null);\n\n // Use controlled or uncontrolled mode\n const currentFile = selectedFile ?? internalFile;\n const hasFile = currentFile !== null;\n\n const handleButtonClick = () => {\n if (!disabled) {\n fileInputRef.current?.click();\n }\n };\n\n const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n\n if (!file) return;\n\n // Validate file type\n const acceptedTypes = accept.split(',').map((type) => type.trim());\n const isValidType = acceptedTypes.some((type) => {\n if (type.endsWith('/*')) {\n const mainType = type.split('/')[0];\n return file.type.startsWith(mainType + '/');\n }\n if (type.startsWith('.')) {\n return file.name.toLowerCase().endsWith(type.toLowerCase());\n }\n return file.type === type;\n });\n\n if (!isValidType) {\n onTypeError?.(file);\n return;\n }\n\n // Validate file size\n if (maxSize && file.size > maxSize) {\n onSizeError?.(file, maxSize);\n return;\n }\n\n // Update state\n if (selectedFile === undefined) {\n setInternalFile(file);\n }\n onFileSelect?.(file);\n\n // Reset input value to allow selecting the same file again\n event.target.value = '';\n };\n\n const handleRemoveFile = () => {\n if (!disabled) {\n if (selectedFile === undefined) {\n setInternalFile(null);\n }\n onRemoveFile?.();\n }\n };\n\n if (variant === 'compact') {\n return (\n <div className={cn('inline-flex items-center gap-2', className)}>\n {hasFile ? (\n <div className=\"inline-flex items-center gap-2 bg-muted rounded-md px-3 py-1.5\">\n <PaperclipIcon className=\"h-4 w-4 text-text-800\" />\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-800\">\n {currentFile.name}\n </Text>\n <button\n type=\"button\"\n onClick={handleRemoveFile}\n disabled={disabled}\n className=\"hover:opacity-70 disabled:cursor-not-allowed disabled:opacity-50\"\n aria-label=\"Remover imagem\"\n title=\"Remover imagem\"\n >\n <XIcon className=\"h-3 w-3 text-primary-950\" />\n </button>\n </div>\n ) : (\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"extra-small\"\n onClick={handleButtonClick}\n disabled={disabled}\n >\n {buttonIcon || <ImageIcon className=\"h-4 w-4\" />}\n <span className=\"ml-2\">{buttonText}</span>\n </Button>\n )}\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={accept}\n className=\"hidden\"\n onChange={handleFileChange}\n disabled={disabled}\n />\n </div>\n );\n }\n\n return (\n <div className={cn('space-y-3', className)}>\n {hasFile ? (\n <div className=\"space-y-2\">\n <div className=\"inline-flex items-center gap-2 bg-muted rounded-md px-3 py-2\">\n <PaperclipIcon className=\"h-4 w-4 text-text-800\" />\n <Text size=\"sm\" weight=\"medium\" className=\"text-text-800\">\n {currentFile.name}\n </Text>\n <button\n type=\"button\"\n onClick={handleRemoveFile}\n disabled={disabled}\n className=\"hover:opacity-70 disabled:cursor-not-allowed disabled:opacity-50\"\n aria-label=\"Remover imagem\"\n title=\"Remover imagem\"\n >\n <XIcon className=\"h-4 w-4 text-primary-950 cursor-pointer\" />\n </button>\n </div>\n </div>\n ) : (\n <Button\n type=\"button\"\n variant=\"link\"\n className=\"gap-1.5\"\n onClick={handleButtonClick}\n disabled={disabled}\n >\n {buttonIcon || <ImageIcon className=\"h-4 w-4\" />}\n {buttonText}\n </Button>\n )}\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={accept}\n className=\"hidden\"\n onChange={handleFileChange}\n disabled={disabled}\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}