diff --git a/example_file_drop.py b/example_file_drop.py
index d3420c9..23eab85 100644
--- a/example_file_drop.py
+++ b/example_file_drop.py
@@ -4,6 +4,7 @@ import base64
from PIL import Image
import io
from niceguiex.components import FileDrop, ImageDrop
+from niceguiex.components.file_drop import FileData, ImageData
@ui.page('/')
@@ -13,14 +14,12 @@ async def main_page():
# Example 1: Multiple files with content
uploaded_files = ui.column().classes('w-full mt-4')
- def handle_multiple_files(files: List[Dict[str, Any]]):
+ def handle_multiple_files(file: FileData):
with uploaded_files:
- ui.label(f'Uploaded {len(files)} file(s):').classes('font-bold mb-2')
- for file in files:
- with ui.card().classes('p-2 mb-2'):
- ui.label(f"📄 {file['name']}").classes('font-medium')
- ui.label(f"Size: {file['size']:,} bytes").classes('text-sm text-gray-600')
- ui.label(f"Type: {file['type']}").classes('text-sm text-gray-600')
+ with ui.card().classes('p-2 mb-2'):
+ ui.label(f"📄 {file['name']}").classes('font-medium')
+ ui.label(f"Size: {file['size']:,} bytes").classes('text-sm text-gray-600')
+ ui.label(f"Type: {file['file_type']}").classes('text-sm text-gray-600')
ui.label('Multiple Files (returns list)').classes('text-h6 mb-2')
FileDrop(
@@ -73,16 +72,16 @@ async def main_page():
# Example 4: Image drop with preview
image_preview = ui.column().classes('w-full mt-4')
- def handle_image(img: Image.Image):
- print(f"Image uploaded: {img.format}, Size: {img.size}")
+ def handle_image(img: ImageData):
+ print(f"Image uploaded: {img['image'].format}, Size: {img['size']}")
image_preview.clear()
with image_preview:
- ui.label(f"Image: {img.format} - {img.size[0]}x{img.size[1]}px").classes('mb-2')
+ ui.label(f"Image: {img['image'].format} - {img['image'].size[0]}x{img['image'].size[1]}px").classes('mb-2')
# Convert PIL image back to base64 for display
buffered = io.BytesIO()
- img.save(buffered, format=img.format or 'PNG')
+ img['image'].save(buffered, format=img['image'].format or 'PNG')
img_data = base64.b64encode(buffered.getvalue()).decode()
- ui.html(f'
')
+ ui.html(f'
')
ui.label('Image Drop (returns PIL Image)').classes('text-h6 mb-2')
ImageDrop(
diff --git a/src/niceguiex/components/__init__.py b/src/niceguiex/components/__init__.py
index 1c43d5f..88c13d2 100644
--- a/src/niceguiex/components/__init__.py
+++ b/src/niceguiex/components/__init__.py
@@ -1,5 +1,5 @@
from .auto_scroll_area import AutoScrollArea
from .chat_input import ChatInput
-from .file_drop import FileDrop, ImageDrop
+from .file_drop import FileDrop, ImageDrop, FileData, ImageData
-__all__ = ['AutoScrollArea', 'ChatInput', 'FileDrop', 'ImageDrop']
+__all__ = ['AutoScrollArea', 'ChatInput', 'FileDrop', 'ImageDrop', 'FileData', 'ImageData']
diff --git a/src/niceguiex/components/file_drop.py b/src/niceguiex/components/file_drop.py
index 2e03f69..6972034 100644
--- a/src/niceguiex/components/file_drop.py
+++ b/src/niceguiex/components/file_drop.py
@@ -1,11 +1,26 @@
from nicegui import ui
-from typing import Optional, Callable
+from typing import Optional, Callable, TypedDict, Any
from pathlib import Path
import tempfile
from PIL import Image
import io
+class FileData(TypedDict):
+ name: str
+ size: int
+ file_type: str
+ content: Optional[Any]
+ path: Optional[Path]
+
+
+class ImageData(TypedDict):
+ name: str
+ size: int
+ file_type: str
+ image: Image.Image
+
+
class FileDrop(ui.element):
def __init__(self,
on_upload: Optional[Callable] = None,
@@ -100,24 +115,13 @@ class FileDrop(ui.element):
# Process single file upload
if hasattr(e, 'content'):
+ print('content')
file_data = self._process_file(e)
if file_data:
# For single file mode, return the dict directly
- if not self.multiple:
- self.on_upload_callback(file_data)
- else:
- self.on_upload_callback([file_data])
- # Handle multiple files
- elif hasattr(e, 'files'):
- files = []
- for file_info in e.files:
- file_data = self._process_file(file_info)
- if file_data:
- files.append(file_data)
- if files:
- self.on_upload_callback(files)
+ self.on_upload_callback(file_data)
- def _process_file(self, file_obj):
+ def _process_file(self, file_obj) -> FileData | None:
"""Process a single file object"""
if not hasattr(file_obj, 'content'):
return None
@@ -130,11 +134,11 @@ class FileDrop(ui.element):
ui.notify(f"File too large. Max size is {self.max_size}MB", type='negative')
return None
- file_data = {
- 'name': file_obj.name if hasattr(file_obj, 'name') else 'unknown',
- 'size': size,
- 'type': file_obj.type if hasattr(file_obj, 'type') else 'application/octet-stream',
- }
+ file_data: FileData = {'name': file_obj.name if hasattr(file_obj, 'name') else 'unknown',
+ 'size': size,
+ 'file_type': file_obj.type if hasattr(file_obj, 'type') else 'application/octet-stream',
+ 'content': None,
+ 'path': None}
# Return content or temp file path based on settings
if self.return_content:
@@ -171,7 +175,7 @@ class ImageDrop(FileDrop):
*args, **kwargs
)
- def _handle_image_upload(self, data):
+ def _handle_image_upload(self, data: FileData):
"""Convert file data to PIL Images before calling user callback"""
if self._user_callback:
if isinstance(data, list) and len(data) == 1:
@@ -186,5 +190,9 @@ class ImageDrop(FileDrop):
self._user_callback(images)
else:
# Single image - convert to PIL Image
- img = Image.open(io.BytesIO(data['content']))
- self._user_callback(img)
+ image_data: ImageData = {'name': data['name'],
+ 'size': data['size'],
+ 'file_type': data['file_type'],
+ 'image': Image.open(io.BytesIO(data['content']))}
+ # img = Image.open(io.BytesIO(data['content']))
+ self._user_callback(image_data)