UNPKG

zentrixui

Version:

ZentrixUI - A modern, highly customizable and accessible React file upload component library with multiple variants, JSON-based configuration, and excellent developer experience.

313 lines (312 loc) 9.32 kB
const getFileExtension = (filename) => { const lastDotIndex = filename.lastIndexOf("."); if (lastDotIndex === -1) return ""; return filename.substring(lastDotIndex + 1).toLowerCase(); }; const validateFileType = (file, allowedTypes = ["*"], allowedExtensions = ["*"]) => { const errors = []; const warnings = []; const fileType = file.type; const fileName = file.name; const fileExtension = getFileExtension(fileName); if (!fileExtension && fileName.indexOf(".") === -1) { warnings.push("File has no extension, type validation may be unreliable"); } if (allowedTypes.includes("*") && allowedExtensions.includes("*")) { return { isValid: true, errors, warnings }; } if (!allowedTypes.includes("*")) { let typeValid = false; for (const allowedType of allowedTypes) { if (allowedType === "*") { typeValid = true; break; } if (allowedType.endsWith("/*")) { const baseType = allowedType.slice(0, -2); if (fileType.startsWith(baseType + "/")) { typeValid = true; break; } } if (fileType === allowedType) { typeValid = true; break; } } if (!typeValid) { errors.push({ code: "invalid-file-type", message: `File type "${fileType}" is not allowed. Allowed types: ${allowedTypes.join(", ")}`, type: "type" }); } } if (!allowedExtensions.includes("*")) { const extensionValid = allowedExtensions.some( (ext) => ext === "*" || ext.toLowerCase() === fileExtension ); if (!extensionValid) { errors.push({ code: "invalid-file-extension", message: `File extension ".${fileExtension}" is not allowed. Allowed extensions: ${allowedExtensions.join(", ")}`, type: "type" }); } } return { isValid: errors.length === 0, errors, warnings }; }; const formatFileSize = (bytes) => { if (bytes === 0) return "0 Bytes"; const k = 1024; const sizes = ["Bytes", "KB", "MB", "GB", "TB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; }; const validateFileSize = (file, maxSize, minSize = 0) => { const errors = []; const warnings = []; if (file.size > maxSize) { errors.push({ code: "file-too-large", message: `File size ${formatFileSize(file.size)} exceeds maximum allowed size of ${formatFileSize(maxSize)}`, type: "size" }); } if (file.size < minSize) { errors.push({ code: "file-too-small", message: `File size ${formatFileSize(file.size)} is below minimum required size of ${formatFileSize(minSize)}`, type: "size" }); } if (file.size > maxSize * 0.8) { warnings.push("File is approaching the maximum size limit"); } if (file.size === 0) { warnings.push("File appears to be empty"); } return { isValid: errors.length === 0, errors, warnings }; }; const validateFileCount = (files, maxFiles, currentFileCount = 0) => { const errors = []; const warnings = []; const totalFiles = files.length + currentFileCount; if (totalFiles > maxFiles) { errors.push({ code: "too-many-files", message: `Cannot upload ${totalFiles} files. Maximum allowed is ${maxFiles}`, type: "count" }); } if (totalFiles > maxFiles * 0.8) { warnings.push(`Approaching file limit (${totalFiles}/${maxFiles})`); } return { isValid: errors.length === 0, errors, warnings }; }; const validateImageDimensions = (file, maxWidth, maxHeight, minWidth, minHeight) => { return new Promise((resolve) => { const errors = []; const warnings = []; if (!file.type.startsWith("image/")) { resolve({ isValid: true, errors, warnings }); return; } const img = new Image(); const url = URL.createObjectURL(file); img.onload = () => { URL.revokeObjectURL(url); const { width, height } = img; if (maxWidth && width > maxWidth) { errors.push({ code: "image-width-too-large", message: `Image width ${width}px exceeds maximum allowed width of ${maxWidth}px`, type: "dimensions" }); } if (maxHeight && height > maxHeight) { errors.push({ code: "image-height-too-large", message: `Image height ${height}px exceeds maximum allowed height of ${maxHeight}px`, type: "dimensions" }); } if (minWidth && width < minWidth) { errors.push({ code: "image-width-too-small", message: `Image width ${width}px is below minimum required width of ${minWidth}px`, type: "dimensions" }); } if (minHeight && height < minHeight) { errors.push({ code: "image-height-too-small", message: `Image height ${height}px is below minimum required height of ${minHeight}px`, type: "dimensions" }); } resolve({ isValid: errors.length === 0, errors, warnings }); }; img.onerror = () => { URL.revokeObjectURL(url); errors.push({ code: "invalid-image", message: "Unable to read image file or file is corrupted", type: "validation" }); resolve({ isValid: false, errors, warnings }); }; img.src = url; }); }; const validateFile = async (file, config, currentFileCount = 0) => { const allErrors = []; const allWarnings = []; const typeValidation = validateFileType( file, config.validation.allowedTypes, config.validation.allowedExtensions ); allErrors.push(...typeValidation.errors); allWarnings.push(...typeValidation.warnings); const sizeValidation = validateFileSize( file, config.validation.maxSize, config.validation.minSize || 0 ); allErrors.push(...sizeValidation.errors); allWarnings.push(...sizeValidation.warnings); if (config.validation.validateDimensions && file.type.startsWith("image/")) { const dimensionValidation = await validateImageDimensions( file, config.validation.maxWidth, config.validation.maxHeight, config.validation.minWidth, config.validation.minHeight ); allErrors.push(...dimensionValidation.errors); allWarnings.push(...dimensionValidation.warnings); } return { isValid: allErrors.length === 0, errors: allErrors, warnings: allWarnings }; }; const validateFiles = async (files, config, currentFileCount = 0) => { const validFiles = []; const rejectedFiles = []; const allWarnings = []; let totalSize = 0; const countValidation = validateFileCount(files, config.validation.maxFiles, currentFileCount); if (!countValidation.isValid) { const countError = countValidation.errors[0]; rejectedFiles.push(...files.map((file) => ({ file, errors: [countError] }))); return { validFiles: [], rejectedFiles, totalSize: 0, warnings: countValidation.warnings }; } allWarnings.push(...countValidation.warnings); for (const file of files) { const validation = await validateFile(file, config, currentFileCount + validFiles.length); if (validation.isValid) { validFiles.push(file); totalSize += file.size; } else { rejectedFiles.push({ file, errors: validation.errors }); } allWarnings.push(...validation.warnings); } return { validFiles, rejectedFiles, totalSize, warnings: allWarnings }; }; const isImageFile = (file) => { return file.type.startsWith("image/"); }; const isVideoFile = (file) => { return file.type.startsWith("video/"); }; const isAudioFile = (file) => { return file.type.startsWith("audio/"); }; const isDocumentFile = (file) => { const documentTypes = [ "application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "text/plain", "text/csv", "application/rtf" ]; return documentTypes.includes(file.type); }; const createValidationError = (code, message, type) => { return { code, message, type }; }; const validateAcceptAttribute = (accept) => { if (!accept || accept === "*") return true; const parts = accept.split(",").map((part) => part.trim()); for (const part of parts) { if (part.startsWith(".")) { if (!/^\.[a-zA-Z0-9]+$/.test(part)) return false; } else if (part.includes("/")) { const [type, subtype] = part.split("/"); if (!type || !subtype) return false; if (!/^[a-zA-Z0-9\-]+$/.test(type)) return false; if (!/^[a-zA-Z0-9\-*]+$/.test(subtype)) return false; } else { return false; } } return true; }; export { createValidationError, formatFileSize, getFileExtension, isAudioFile, isDocumentFile, isImageFile, isVideoFile, validateAcceptAttribute, validateFile, validateFileCount, validateFileSize, validateFileType, validateFiles, validateImageDimensions }; //# sourceMappingURL=file-validation.js.map