analytica-frontend-lib
Version:
Repositório público dos componentes utilizados nas plataformas da Analytica Ensino
1 lines • 20.2 kB
Source Map (JSON)
{"version":3,"sources":["../src/components/FileDropzone/FileDropzone.tsx"],"sourcesContent":["import type React from 'react';\nimport { useRef, useState, useCallback, useId, useEffect } from 'react';\nimport { UploadIcon } from '@phosphor-icons/react/dist/csr/Upload';\nimport { WarningCircleIcon } from '@phosphor-icons/react/dist/csr/WarningCircle';\nimport { CheckCircleIcon } from '@phosphor-icons/react/dist/csr/CheckCircle';\nimport { FileVideoIcon } from '@phosphor-icons/react/dist/csr/FileVideo';\nimport { FileAudioIcon } from '@phosphor-icons/react/dist/csr/FileAudio';\nimport { FilePdfIcon } from '@phosphor-icons/react/dist/csr/FilePdf';\nimport { ClosedCaptioningIcon } from '@phosphor-icons/react/dist/csr/ClosedCaptioning';\nimport { PaperclipIcon } from '@phosphor-icons/react/dist/csr/Paperclip';\nimport { PencilSimpleIcon } from '@phosphor-icons/react/dist/csr/PencilSimple';\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 type FileType = 'image' | 'video' | 'audio' | 'pdf' | 'subtitle';\n\nexport interface FileDropzoneProps {\n /** Label text displayed above the dropzone */\n label?: string;\n /** Helper text displayed below the dropzone */\n helperText?: string;\n /** Error message displayed below the dropzone */\n errorMessage?: string;\n /** Current file URL (for existing files) */\n fileUrl?: string | null;\n /** File currently selected */\n selectedFile?: File | null;\n /** Callback when a file is selected */\n onFileSelect?: (file: File) => void;\n /** Callback when file is removed */\n onRemoveFile?: () => void;\n /** Accept specific file types (required) */\n accept: string;\n /** Type of file for display purposes */\n fileType: FileType;\n /** Action text shown as link (default: \"Clique aqui\") */\n actionText?: string;\n /** Description text shown after action text */\n placeholder?: string;\n /** Text shown when hovering over an existing file */\n changeText?: string;\n /** Label of the replace button shown in the image preview state (default: \"Trocar\") */\n changeButtonText?: string;\n /** Disabled state */\n disabled?: boolean;\n /** Required field indicator */\n required?: 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 /** Show image preview (only for image type) */\n showPreview?: boolean;\n /** Max height for image preview */\n previewMaxHeight?: string;\n}\n\nconst FILE_TYPE_CONFIG: Record<\n FileType,\n {\n icon: React.ElementType;\n defaultPlaceholder: string;\n }\n> = {\n image: {\n icon: UploadIcon,\n defaultPlaceholder: 'para subir a imagem ou arraste aqui',\n },\n video: {\n icon: FileVideoIcon,\n defaultPlaceholder: 'para subir o vídeo ou arraste aqui',\n },\n audio: {\n icon: FileAudioIcon,\n defaultPlaceholder: 'para subir o áudio ou arraste aqui',\n },\n pdf: {\n icon: FilePdfIcon,\n defaultPlaceholder: 'para subir o PDF ou arraste aqui',\n },\n subtitle: {\n icon: ClosedCaptioningIcon,\n defaultPlaceholder: 'para subir a legenda ou arraste aqui',\n },\n};\n\n/**\n * FileDropzone component for Analytica Ensino platforms\n *\n * A generic dropzone component for file upload with drag-and-drop support,\n * file preview, and dashed border styling. Supports multiple file types.\n *\n * @example\n * ```tsx\n * // Video upload\n * <FileDropzone\n * label=\"Arquivo do vídeo\"\n * accept=\"video/mp4,.mp4\"\n * fileType=\"video\"\n * onFileSelect={(file) => setVideoFile(file)}\n * />\n *\n * // Audio upload\n * <FileDropzone\n * label=\"Arquivo do podcast\"\n * accept=\"audio/*,.mp3,.wav\"\n * fileType=\"audio\"\n * onFileSelect={(file) => setAudioFile(file)}\n * />\n *\n * // Image upload with preview\n * <FileDropzone\n * label=\"Quadro inicial\"\n * accept=\"image/*\"\n * fileType=\"image\"\n * showPreview={true}\n * onFileSelect={(file) => setImageFile(file)}\n * />\n * ```\n */\nexport default function FileDropzone({\n label,\n helperText,\n errorMessage,\n fileUrl,\n selectedFile,\n onFileSelect,\n onRemoveFile,\n accept,\n fileType,\n actionText = 'Clique aqui',\n placeholder,\n changeText,\n changeButtonText = 'Trocar',\n disabled = false,\n required = false,\n className,\n maxSize,\n onSizeError,\n onTypeError,\n showPreview = true,\n previewMaxHeight = 'max-h-48',\n}: Readonly<FileDropzoneProps>) {\n const fileInputRef = useRef<HTMLInputElement>(null);\n const dragCounterRef = useRef(0);\n const [isDragging, setIsDragging] = useState(false);\n const [previewUrl, setPreviewUrl] = useState<string | null>(null);\n const generatedId = useId();\n const inputId = `file-dropzone-${generatedId}`;\n\n const hasError = Boolean(errorMessage);\n const config = FILE_TYPE_CONFIG[fileType];\n const IconComponent = config.icon;\n const defaultPlaceholder = placeholder || config.defaultPlaceholder;\n const defaultChangeText =\n changeText ||\n (fileType === 'image'\n ? 'Clique para alterar a imagem'\n : 'Clique para trocar o arquivo');\n\n // Manage object URL for selectedFile with proper cleanup (only for images)\n useEffect(() => {\n if (!selectedFile || fileType !== 'image') {\n setPreviewUrl(null);\n return;\n }\n\n const url = URL.createObjectURL(selectedFile);\n setPreviewUrl(url);\n\n return () => {\n URL.revokeObjectURL(url);\n };\n }, [selectedFile, fileType]);\n\n const displayUrl = fileType === 'image' ? previewUrl || fileUrl : null;\n const hasFile = Boolean(selectedFile || fileUrl);\n\n const validateFile = useCallback(\n (file: File): boolean => {\n // Validate file type\n const acceptedTypes = accept.split(',').map((type) => type.trim());\n const isValidType = acceptedTypes.some((type) => {\n if (type.startsWith('.')) {\n return file.name.toLowerCase().endsWith(type.toLowerCase());\n }\n if (!file.type) {\n return false;\n }\n if (type.endsWith('/*')) {\n const mainType = type.split('/')[0];\n return file.type.startsWith(mainType + '/');\n }\n return file.type === type;\n });\n\n if (!isValidType) {\n onTypeError?.(file);\n return false;\n }\n\n // Validate file size\n if (maxSize != null && file.size > maxSize) {\n onSizeError?.(file, maxSize);\n return false;\n }\n\n return true;\n },\n [accept, maxSize, onSizeError, onTypeError]\n );\n\n const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n if (!file || disabled) return;\n\n if (validateFile(file)) {\n onFileSelect?.(file);\n }\n\n // Reset input value to allow selecting the same file again\n event.target.value = '';\n };\n\n const handleDragEnter = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounterRef.current += 1;\n if (!disabled && dragCounterRef.current === 1) {\n setIsDragging(true);\n }\n };\n\n const handleDragLeave = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounterRef.current -= 1;\n if (dragCounterRef.current === 0) {\n setIsDragging(false);\n }\n };\n\n const handleDragOver = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n };\n\n const handleDrop = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounterRef.current = 0;\n setIsDragging(false);\n\n if (disabled) return;\n\n const file = e.dataTransfer.files?.[0];\n if (!file) return;\n\n if (validateFile(file)) {\n onFileSelect?.(file);\n }\n };\n\n const handleRemove = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (!disabled) {\n onRemoveFile?.();\n }\n };\n\n const handleChangeClick = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (!disabled) {\n fileInputRef.current?.click();\n }\n };\n\n const fileName = selectedFile?.name || (fileUrl ? 'Arquivo carregado' : '');\n\n const getBorderClasses = () => {\n if (hasError) {\n return disabled\n ? 'border-indicator-error'\n : 'border-indicator-error hover:border-indicator-error';\n }\n if (isDragging) {\n return 'border-primary-500 bg-primary-50';\n }\n return disabled\n ? 'border-border-200'\n : 'border-border-200 hover:border-primary-500';\n };\n\n const dropzoneClasses = cn(\n 'flex flex-col items-center justify-center p-6 border-2 border-dashed rounded-lg cursor-pointer transition-colors',\n getBorderClasses(),\n disabled && 'cursor-not-allowed opacity-50'\n );\n\n // Image preview state (Figma): solid card with preview + file chip + actions\n const isImagePreview =\n fileType === 'image' && showPreview && Boolean(displayUrl);\n\n const imageCardClasses = cn(\n 'flex flex-col items-center justify-center gap-3 p-6 rounded-lg bg-background-100',\n hasError && 'border-2 border-indicator-error',\n disabled && 'opacity-50'\n );\n\n const getAriaDescribedBy = (): string | undefined => {\n if (errorMessage) return `${inputId}-error`;\n if (helperText) return `${inputId}-helper`;\n return undefined;\n };\n\n // Image preview state (Figma): preview + file chip (with remove) + replace button\n const renderImagePreview = () => (\n <>\n <img\n src={displayUrl ?? undefined}\n alt=\"Preview do arquivo\"\n className={cn('max-w-full rounded-lg object-contain', previewMaxHeight)}\n />\n\n <div className=\"flex items-center gap-2 rounded-full border border-border-200 bg-background px-3 py-1.5\">\n <PaperclipIcon size={16} className=\"text-text-600\" />\n <Text size=\"sm\" className=\"text-text-700 max-w-[200px] truncate\">\n {fileName}\n </Text>\n {onRemoveFile && !disabled && (\n <Button\n variant=\"raw\"\n type=\"button\"\n onClick={handleRemove}\n className=\"p-0.5 rounded-full hover:bg-error-100 transition-colors\"\n aria-label=\"Remover arquivo\"\n >\n <XIcon size={14} className=\"text-indicator-error\" />\n </Button>\n )}\n </div>\n\n {!disabled && (\n <Button\n variant=\"outline\"\n action=\"primary\"\n size=\"small\"\n iconLeft={<PencilSimpleIcon size={16} />}\n onClick={handleChangeClick}\n >\n {changeButtonText}\n </Button>\n )}\n </>\n );\n\n const renderFilePreview = () => {\n // For non-image files or images without preview - show file info with check\n if (hasFile) {\n return (\n <div className=\"flex flex-col items-center gap-3\">\n <div className=\"flex items-center justify-center w-12 h-12 rounded-full bg-success-100\">\n <CheckCircleIcon\n size={28}\n className=\"text-success-600\"\n weight=\"fill\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <IconComponent size={20} className=\"text-text-600\" />\n <Text size=\"sm\" className=\"text-text-700 max-w-[200px] truncate\">\n {fileName}\n </Text>\n {onRemoveFile && !disabled && (\n <Button\n variant=\"raw\"\n type=\"button\"\n onClick={handleRemove}\n className=\"p-1 rounded-full hover:bg-error-100 transition-colors\"\n aria-label=\"Remover arquivo\"\n >\n <XIcon size={14} className=\"text-indicator-error\" />\n </Button>\n )}\n </div>\n <Text size=\"xs\" className=\"text-text-500\">\n {defaultChangeText}\n </Text>\n </div>\n );\n }\n\n // Empty state\n return (\n <>\n <IconComponent size={24} className=\"text-primary-500 mb-2\" />\n <Text size=\"sm\" className=\"text-text-600 text-center\">\n <span className=\"text-primary-500 font-medium\">{actionText}</span>{' '}\n {defaultPlaceholder}\n </Text>\n </>\n );\n };\n\n return (\n <div className={className}>\n {label && (\n <label\n htmlFor={inputId}\n className=\"block font-bold text-text-900 mb-2 text-sm\"\n >\n {label} {required && <span className=\"text-indicator-error\">*</span>}\n </label>\n )}\n\n <input\n ref={fileInputRef}\n id={inputId}\n type=\"file\"\n accept={accept}\n onChange={handleFileChange}\n className=\"hidden\"\n disabled={disabled}\n aria-invalid={hasError}\n aria-describedby={getAriaDescribedBy()}\n />\n\n {isImagePreview ? (\n <div\n className={imageCardClasses}\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n >\n {renderImagePreview()}\n </div>\n ) : (\n <label\n htmlFor={inputId}\n className={dropzoneClasses}\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n >\n {renderFilePreview()}\n </label>\n )}\n\n <div className=\"mt-1\">\n {helperText && !errorMessage && (\n <Text id={`${inputId}-helper`} size=\"xs\" className=\"text-text-500\">\n {helperText}\n </Text>\n )}\n {errorMessage && (\n <Text id={`${inputId}-error`} size=\"xs\" color=\"text-indicator-error\">\n <WarningCircleIcon size={14} /> {errorMessage}\n </Text>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}