UNPKG

test-crud

Version:

es una prueba acerca de como publicar un package name

238 lines (219 loc) 7.05 kB
import { useCallback, useEffect, useMemo, useState } from 'react' import { useFormikContext } from 'formik' import { useDropzone } from 'react-dropzone' import { Box, Typography, List, ListItem, ListItemText, IconButton, FormHelperText, } from '@mui/material' import DeleteIcon from '@mui/icons-material/Delete' import ChangeCircleIcon from '@mui/icons-material/ChangeCircle' import CloudUploadIcon from '@mui/icons-material/CloudUpload' const EXTENSION_MIME_MAP = { PDF: 'application/pdf', DOC: 'application/msword', DOCX: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', TXT: 'text/plain', PNG: 'image/png', JPG: 'image/jpeg', JPEG: 'image/jpeg', XLS: 'application/vnd.ms-excel', XLSX: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', } function AppDropBox({ name, label = '', acceptedFormats = ['PDF'], multiple = true, showIcon = true, fileInfo = {}, editable = true, }) { const { setFieldValue, setFieldTouched, errors, touched, values } = useFormikContext() const [fileExist, setFileExist] = useState(false) // Para controlar si está el archivo precargado // Archivos actuales en Formik const files = values[name] || [] // 🔑 Definir si mostrar archivo precargado o los subidos const showPreloadedFile = fileExist && fileInfo?.primaryLabel const showUploadedFiles = files.length > 0 // Al cargar el componente, si hay archivo precargado, activar fileExist useEffect(() => { if (fileInfo?.primaryLabel) setFileExist(true) else setFileExist(false) }, [fileInfo]) // Mapear extensiones a MIME types const acceptedMimes = useMemo(() => { return acceptedFormats .map((ext) => EXTENSION_MIME_MAP[ext.toUpperCase()]) .filter(Boolean) }, [acceptedFormats]) // MIME types para Dropzone const acceptedMimeObj = useMemo(() => { return acceptedMimes.reduce((acc, mime) => { acc[mime] = [] return acc }, {}) }, [acceptedMimes]) // Verificar duplicados const isDuplicate = (newFile) => { return files.some( (file) => file.name === newFile.name && file.size === newFile.size ) } // Añadir o reemplazar archivos const onDrop = useCallback( (acceptedFiles, indexToReplace = null) => { setFieldTouched(name, true) const filteredFiles = acceptedFiles.filter( (file) => acceptedMimes.includes(file.type) && !isDuplicate(file) ) if (filteredFiles.length === 0) return // Al subir archivo nuevo, quitar precargado if (fileExist) setFileExist(false) if (indexToReplace !== null) { const newFiles = [...files] newFiles[indexToReplace] = filteredFiles[0] setFieldValue(name, newFiles) } else { setFieldValue( name, multiple ? [...files, ...filteredFiles] : filteredFiles.slice(0, 1) ) } }, [ name, setFieldTouched, setFieldValue, files, multiple, acceptedMimes, fileExist, ] ) // Eliminar archivo const handleRemoveFile = (index) => { const newFiles = files.filter((_, i) => i !== index) setFieldValue(name, newFiles) } // Configuración Dropzone const { getRootProps, getInputProps } = useDropzone({ onDrop: (acceptedFiles) => onDrop(acceptedFiles), accept: acceptedMimeObj, multiple, }) return ( <div> <div className="dropbox"> <Typography variant="h6">{label}</Typography> {/* Solo mostrar dropzone si no hay archivos ni archivo precargado */} {!showPreloadedFile && !showUploadedFiles && ( <Box {...getRootProps()} sx={{ border: '2px dashed #aaa', cursor: 'pointer', borderRadius: 2, display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'row', minHeight: '40px', width: '100%', textAlign: 'center', }} > <input {...getInputProps()} /> {showIcon && ( <CloudUploadIcon fontSize="small" color="action" sx={{ marginRight: 1 }} /> )} <Typography variant="body2" sx={{ lineHeight: 1 }}> Selecciona o arrastra un archivo </Typography> </Box> )} </div> {/* Mostrar archivos */} <List sx={{ padding: 0, margin: 0 }}> {showPreloadedFile ? ( <ListItem> <ListItemText primary={fileInfo.primaryLabel} secondary={fileInfo.secondaryLabel} /> {/* Botones adicionales */} {fileInfo?.buttonProps?.map((btn, index) => ( <span key={index}>{btn.button}</span> ))} {editable && ( <> <input type="file" accept={acceptedMimes.join(',')} style={{ display: 'none' }} id="file-input-reemplazo" onChange={(event) => { if (event.target.files.length > 0) { onDrop([event.target.files[0]]) } }} /> <label htmlFor="file-input-reemplazo"> <IconButton edge="end" component="span" title="Reemplazar"> <ChangeCircleIcon color="primary" /> </IconButton> </label> </> )} </ListItem> ) : showUploadedFiles ? ( files.map((file, index) => ( <ListItem key={index}> <ListItemText primary={file.name} secondary={`${(file.size / 1024).toFixed(2)} KB`} /> <input type="file" accept={acceptedMimes.join(',')} style={{ display: 'none' }} id={`file-input-${index}`} onChange={(event) => { if (event.target.files.length > 0) { onDrop([event.target.files[0]], index) } }} /> <label htmlFor={`file-input-${index}`}> <IconButton edge="end" component="span" title="Reemplazar"> <ChangeCircleIcon color="primary" /> </IconButton> </label> {/* Eliminar */} <IconButton edge="end" onClick={() => handleRemoveFile(index)} title="Eliminar" > <DeleteIcon color="error" /> </IconButton> </ListItem> )) ) : null} </List> {touched[name] && errors[name] && ( <FormHelperText error>{errors[name]}</FormHelperText> )} </div> ) } export default AppDropBox