mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: restrict SVG-to-raster dropzone to only accept SVG files
Adds fileFilter and acceptDescription props to Dropzone so tools can restrict accepted file types across all entry paths (file picker, drag-and-drop, paste, single URL import, bulk URL import).
This commit is contained in:
@@ -88,6 +88,10 @@ interface DropzoneProps {
|
|||||||
currentFiles?: File[];
|
currentFiles?: File[];
|
||||||
/** Use a smaller layout that fits constrained containers like the pipeline preview. */
|
/** Use a smaller layout that fits constrained containers like the pipeline preview. */
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
|
/** Override the default isImageFile check for drag/drop and paste. */
|
||||||
|
fileFilter?: (file: File) => boolean;
|
||||||
|
/** Override the format hint text (e.g. "SVG files only"). */
|
||||||
|
acceptDescription?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Browsers may not map certain formats (HEIC, JXL, RAW, etc.) to image/* in file pickers.
|
// Browsers may not map certain formats (HEIC, JXL, RAW, etc.) to image/* in file pickers.
|
||||||
@@ -104,7 +108,10 @@ export function Dropzone({
|
|||||||
multiple = true,
|
multiple = true,
|
||||||
currentFiles = [],
|
currentFiles = [],
|
||||||
compact = false,
|
compact = false,
|
||||||
|
fileFilter,
|
||||||
|
acceptDescription,
|
||||||
}: DropzoneProps) {
|
}: DropzoneProps) {
|
||||||
|
const checkFile = fileFilter ?? isImageFile;
|
||||||
const resolvedAccept = expandAccept(accept);
|
const resolvedAccept = expandAccept(accept);
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [urlInput, setUrlInput] = useState("");
|
const [urlInput, setUrlInput] = useState("");
|
||||||
@@ -121,13 +128,17 @@ export function Dropzone({
|
|||||||
setUrlError(null);
|
setUrlError(null);
|
||||||
const file = await importSingleUrl(url);
|
const file = await importSingleUrl(url);
|
||||||
if (file) {
|
if (file) {
|
||||||
setUrlInput("");
|
if (!checkFile(file)) {
|
||||||
onUrlImport?.(file);
|
setUrlError(acceptDescription ?? "This file type is not supported by this tool");
|
||||||
|
} else {
|
||||||
|
setUrlInput("");
|
||||||
|
onUrlImport?.(file);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setUrlError("Could not fetch image from URL");
|
setUrlError("Could not fetch image from URL");
|
||||||
}
|
}
|
||||||
setUrlLoading(false);
|
setUrlLoading(false);
|
||||||
}, [urlInput, importSingleUrl, onUrlImport]);
|
}, [urlInput, importSingleUrl, onUrlImport, checkFile, acceptDescription]);
|
||||||
|
|
||||||
const handleDrag = useCallback((e: DragEvent) => {
|
const handleDrag = useCallback((e: DragEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -141,10 +152,10 @@ export function Dropzone({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setIsDragging(false);
|
setIsDragging(false);
|
||||||
const files = Array.from(e.dataTransfer.files).filter(isImageFile);
|
const files = Array.from(e.dataTransfer.files).filter(checkFile);
|
||||||
if (files.length > 0) onFiles?.(files);
|
if (files.length > 0) onFiles?.(files);
|
||||||
},
|
},
|
||||||
[onFiles],
|
[onFiles, checkFile],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
@@ -153,7 +164,7 @@ export function Dropzone({
|
|||||||
input.multiple = multiple;
|
input.multiple = multiple;
|
||||||
if (resolvedAccept) input.accept = resolvedAccept;
|
if (resolvedAccept) input.accept = resolvedAccept;
|
||||||
input.onchange = (e) => {
|
input.onchange = (e) => {
|
||||||
const files = Array.from((e.target as HTMLInputElement).files || []);
|
const files = Array.from((e.target as HTMLInputElement).files || []).filter(checkFile);
|
||||||
if (files.length > 0) onFiles?.(files);
|
if (files.length > 0) onFiles?.(files);
|
||||||
};
|
};
|
||||||
input.click();
|
input.click();
|
||||||
@@ -168,13 +179,13 @@ export function Dropzone({
|
|||||||
|
|
||||||
if (clip.files.length > 0) {
|
if (clip.files.length > 0) {
|
||||||
for (const file of clip.files) {
|
for (const file of clip.files) {
|
||||||
if (isImageFile(file)) files.push(file);
|
if (checkFile(file)) files.push(file);
|
||||||
}
|
}
|
||||||
} else if (clip.items) {
|
} else if (clip.items) {
|
||||||
for (const item of clip.items) {
|
for (const item of clip.items) {
|
||||||
if (item.kind === "file") {
|
if (item.kind === "file") {
|
||||||
const file = item.getAsFile();
|
const file = item.getAsFile();
|
||||||
if (file && isImageFile(file)) files.push(file);
|
if (file && checkFile(file)) files.push(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,7 +197,7 @@ export function Dropzone({
|
|||||||
};
|
};
|
||||||
document.addEventListener("paste", handlePaste);
|
document.addEventListener("paste", handlePaste);
|
||||||
return () => document.removeEventListener("paste", handlePaste);
|
return () => document.removeEventListener("paste", handlePaste);
|
||||||
}, [onFiles]);
|
}, [onFiles, checkFile]);
|
||||||
|
|
||||||
const hasMultipleFiles = currentFiles.length > 1;
|
const hasMultipleFiles = currentFiles.length > 1;
|
||||||
|
|
||||||
@@ -246,7 +257,7 @@ export function Dropzone({
|
|||||||
Upload
|
Upload
|
||||||
</button>
|
</button>
|
||||||
<p className="text-xs text-muted-foreground/50">
|
<p className="text-xs text-muted-foreground/50">
|
||||||
PNG, JPG, WebP, HEIC, RAW, PSD, and 65+ formats
|
{acceptDescription ?? "PNG, JPG, WebP, HEIC, RAW, PSD, and 65+ formats"}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{!compact && onUrlImport && (
|
{!compact && onUrlImport && (
|
||||||
@@ -326,7 +337,9 @@ export function Dropzone({
|
|||||||
<UrlImportModal
|
<UrlImportModal
|
||||||
onClose={() => setShowBulkModal(false)}
|
onClose={() => setShowBulkModal(false)}
|
||||||
onImport={(files) => {
|
onImport={(files) => {
|
||||||
for (const file of files) onUrlImport?.(file);
|
for (const file of files) {
|
||||||
|
if (checkFile(file)) onUrlImport?.(file);
|
||||||
|
}
|
||||||
setShowBulkModal(false);
|
setShowBulkModal(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user