69 lines
2.2 KiB
TypeScript
69 lines
2.2 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect } from "react";
|
|
|
|
const MAX_FILES_PER_UPLOAD = 100;
|
|
|
|
/** Keeps Payload's native multi-file picker from accepting an unbounded batch. */
|
|
export function UploadLimitGuard() {
|
|
useEffect(() => {
|
|
const attached = new WeakSet<HTMLInputElement>();
|
|
|
|
const limitedTransfer = (files: FileList) => {
|
|
const transfer = new DataTransfer();
|
|
Array.from(files)
|
|
.slice(0, MAX_FILES_PER_UPLOAD)
|
|
.forEach((file) => transfer.items.add(file));
|
|
return transfer;
|
|
};
|
|
|
|
const attachToFileInputs = () => {
|
|
document.querySelectorAll<HTMLInputElement>('input[type="file"][multiple]').forEach((input) => {
|
|
if (attached.has(input)) return;
|
|
attached.add(input);
|
|
input.addEventListener("change", () => {
|
|
if (!input.files || input.files.length <= MAX_FILES_PER_UPLOAD) return;
|
|
|
|
try {
|
|
input.files = limitedTransfer(input.files).files;
|
|
} catch {
|
|
// Older browsers can expose a read-only FileList. Payload still
|
|
// receives the original selection there and can process it.
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
const limitDroppedFiles = (event: DragEvent) => {
|
|
const files = event.dataTransfer?.files;
|
|
if (!files || files.length <= MAX_FILES_PER_UPLOAD || !(event.target instanceof Element)) return;
|
|
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
try {
|
|
event.target.dispatchEvent(
|
|
new DragEvent("drop", {
|
|
bubbles: true,
|
|
cancelable: true,
|
|
dataTransfer: limitedTransfer(files),
|
|
}),
|
|
);
|
|
} catch {
|
|
// If a browser cannot construct a replacement drag event, the native
|
|
// picker still enforces the same limit for normal file selection.
|
|
}
|
|
};
|
|
|
|
attachToFileInputs();
|
|
document.addEventListener("drop", limitDroppedFiles, true);
|
|
const observer = new MutationObserver(attachToFileInputs);
|
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
return () => {
|
|
document.removeEventListener("drop", limitDroppedFiles, true);
|
|
observer.disconnect();
|
|
};
|
|
}, []);
|
|
|
|
return null;
|
|
}
|