diff --git a/api/Cargo.lock b/api/Cargo.lock index d48cb1d..3eeb97c 100644 --- a/api/Cargo.lock +++ b/api/Cargo.lock @@ -458,9 +458,9 @@ checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" [[package]] name = "glam" -version = "0.30.9" +version = "0.30.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" [[package]] name = "half" @@ -683,7 +683,7 @@ dependencies = [ "glam 0.27.0", "glam 0.28.0", "glam 0.29.3", - "glam 0.30.9", + "glam 0.30.10", "matrixmultiply", "num-complex", "num-rational", diff --git a/client/src/components/common/DragAndDropZone.tsx b/client/src/components/common/DragAndDropZone.tsx index d2f01c3..34a44b7 100644 --- a/client/src/components/common/DragAndDropZone.tsx +++ b/client/src/components/common/DragAndDropZone.tsx @@ -1,9 +1,11 @@ -import { useDropzone, type Accept } from 'react-dropzone'; +import { useDropzone } from 'react-dropzone'; +import type { FileRejection, Accept } from 'react-dropzone'; import { useEffect, useRef, useState, useCallback } from 'preact/hooks'; import type { ComponentChildren, RefObject } from 'preact'; type DragAndDropZoneProps = { onFileDrop: (file: File) => void | Promise; + onFileReject?: (rejectedFiles: FileRejection[]) => void; accept?: Accept; multiple?: boolean; className?: string; @@ -13,6 +15,7 @@ type DragAndDropZoneProps = { const DragAndDropZone = ({ onFileDrop, + onFileReject, accept, multiple = false, className = '', @@ -63,6 +66,14 @@ const DragAndDropZone = ({ console.error('Error handling dropped files:', error); } }, + onDropRejected: (rejectedFiles) => { + if (!onFileReject) return; + try { + onFileReject(rejectedFiles); + } catch (error) { + console.error('Error handling rejected files:', error); + } + }, onDragEnter: () => updateOverlayRect(), }); diff --git a/client/src/components/images/ImageConverter.tsx b/client/src/components/images/ImageConverter.tsx index 37e23fa..1a6a499 100644 --- a/client/src/components/images/ImageConverter.tsx +++ b/client/src/components/images/ImageConverter.tsx @@ -1,6 +1,6 @@ import { useState, useRef, useEffect } from 'preact/hooks'; import { TargetedEvent } from 'preact/compat'; -import { Accept } from 'react-dropzone'; +import type { Accept, FileRejection } from 'react-dropzone'; import { useWasm } from '@/hooks/useWasm'; import ImageJSRootPreview from './ImageJSRootPreview'; @@ -60,6 +60,25 @@ const ImageConverter = () => { 'image/jpeg': ['.jpeg', '.jpg'], }; + const handleFileReject = (rejectedFiles: FileRejection[]) => { + if (rejectedFiles.length === 0) return; + const reasons = rejectedFiles + .map((rejection) => { + const errors = rejection.errors + .map((err) => { + if (err.code === 'file-invalid-type') { + return `${rejection.file.name}: unsupported type`; + } + return `${rejection.file.name}: ${err.message}`; + }) + .join(', '); + return errors; + }) + .join('; '); + + setErrorMessage(`Rejected files: ${reasons}`); + }; + const cleanupBlobUrls = () => { if (prevSrcUrlRef.current) { URL.revokeObjectURL(prevSrcUrlRef.current); @@ -95,8 +114,8 @@ const ImageConverter = () => { img.bits_per_sample === 16 ? img.pixels_u16() : img.bits_per_sample === 8 - ? img.pixels_u8() - : null; + ? img.pixels_u8() + : null; if (!pixels) { setErrorMessage('Failed to extract pixel data from image'); return; @@ -262,6 +281,7 @@ const ImageConverter = () => { accept={acceptedFileTypes} overlayTargetRef={overlayTargetRef} onFileDrop={processFile} + onFileReject={handleFileReject} className="w-full" > { header={'Converted Image'} aspectRatio={previewsAspectRatios} setAspectRatio={setPreviewsAspectRatios} - error={errorMessage} units={units} mmPerPx={mmPerPx} />