@zag-js/file-upload
Version:
Core logic for the file-upload widget implemented as a state machine
71 lines (70 loc) • 2.6 kB
JavaScript
// 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
};