UNPKG

@zag-js/file-upload

Version:

Core logic for the file-upload widget implemented as a state machine

71 lines (70 loc) 2.6 kB
// src/file-upload.utils.ts import { getEventTarget, getWindow } from "@zag-js/dom-query"; import { isFileEqual, isValidFileSize, isValidFileType } from "@zag-js/file-utils"; function isEventWithFiles(event) { const target = getEventTarget(event); if (!event.dataTransfer) return !!target && "files" in target; return event.dataTransfer.types.some((type) => { return type === "Files" || type === "application/x-moz-file"; }); } function isFilesWithinRange(ctx, incomingCount, currentAcceptedFiles) { const { prop, computed } = ctx; if (!computed("multiple") && incomingCount > 1) return false; if (!computed("multiple") && incomingCount + currentAcceptedFiles.length === 2) return true; if (incomingCount + currentAcceptedFiles.length > prop("maxFiles")) return false; return true; } function getEventFiles(ctx, files, currentAcceptedFiles = [], currentRejectedFiles = []) { const { prop, computed } = ctx; const acceptedFiles = []; const rejectedFiles = []; const validateParams = { acceptedFiles: currentAcceptedFiles, rejectedFiles: currentRejectedFiles }; files.forEach((file) => { const [accepted, acceptError] = isValidFileType(file, computed("acceptAttr")); const [sizeMatch, sizeError] = isValidFileSize(file, prop("minFileSize"), prop("maxFileSize")); const isDuplicate = currentAcceptedFiles.some((f) => isFileEqual(f, file)) || acceptedFiles.some((f) => isFileEqual(f, file)); const validateErrors = prop("validate")?.(file, validateParams); const valid = validateErrors ? validateErrors.length === 0 : true; if (accepted && sizeMatch && valid && !isDuplicate) { acceptedFiles.push(file); } else { const errors = [acceptError, sizeError]; if (isDuplicate) errors.push("FILE_EXISTS"); if (!valid) errors.push(...validateErrors ?? []); rejectedFiles.push({ file, errors: errors.filter(Boolean) }); } }); if (!isFilesWithinRange(ctx, acceptedFiles.length, currentAcceptedFiles)) { acceptedFiles.forEach((file) => { rejectedFiles.push({ file, errors: ["TOO_MANY_FILES"] }); }); acceptedFiles.splice(0); } return { acceptedFiles, rejectedFiles }; } function setInputFiles(inputEl, files) { const win = getWindow(inputEl); try { if ("DataTransfer" in win) { const dataTransfer = new win.DataTransfer(); files.forEach((file) => { dataTransfer.items.add(file); }); inputEl.files = dataTransfer.files; } } catch { } } export { getEventFiles, isEventWithFiles, isFilesWithinRange, setInputFiles };