solara icon indicating copy to clipboard operation
solara copied to clipboard

Feature request: Enhancements to FileDrop

Open ntjess opened this issue 1 year ago • 6 comments

Hi there!

After using FileDrop{Multiple} for a few months, a few limitations keep arising that are difficult to patch:

  • Reuploads of the same file are ignored, and it is quite difficult to "clear" the state of the FileDrop to listen for reuploads
  • Customizing the dropzone appearance is impossible; ideally we can provide our own element from Solara land
  • Users can drop files while the previous drop is still uploading, leading to undefined behavior
  • Most file uploaders allow dropping or clicking, while FileDrop only allows dropping

I ended up implementing a DropOrInputFile{s} which combines logic from FileDrop and InputFile widgets to resolve all these points:

  • Clicking and dropping behaviors are added to a solara activator element passed to the function
  • Clicking the widget brings up a file browser
  • Much easier to use than InputFile, which doesn't allow custom widgets and complicates the process of reading file data
  • Minor improvements like allowing a reupload of the same file name and disabling the widget during upload, though these changes are easily provided as a PR to the existing FileDrop

My question is this: If I were to open source the implementation, would you prefer it as a contrib element in a third-party package, new DropOrInputFile{s} elements within core solara, or a modification of FileDrop/FileDropMultiple?

Source Code for video
@sl.component
def Page():
    DefaultPageStyle()
    text, set_text = sl.use_state(b"")

    def on_file(file: FileInfo):
        set_text(file["file_obj"].read(100))

    with sl.Column(style="width: 350px"):
        DropOrInputFile(on_file=on_file)
        DropOrInputFile(
            sl.Card("Drag onto a custom element", style="background-color: green"),
            on_file=on_file,
        )

    if text:
        sl.HTML("h2", "First 100 bytes of the file:")
        sl.Text(str(text))

https://github.com/user-attachments/assets/3520ebd7-81a4-45d5-ac65-ec2ba42772bb

ntjess avatar Sep 27 '24 16:09 ntjess

Hi Natha,

  • Reuploads of the same file are ignored, and it is quite difficult to "clear" the state of the FileDrop to listen for reuploads

Ok, that sounds like a bug. Do you mean the same filename with different content? Can you give an example how to reproduce that?

  • Customizing the dropzone appearance is impossible; ideally we can provide our own element from Solara land

Yes, what I'd like to have, is the FileDrop to take optional children, where it would render those, instead of the default implementation. We should then also feed it some kinda of overlay element it should render when the drag over event triggers. What do you think?

  • Users can drop files while the previous drop is still uploading, leading to undefined behavior

Another bug, thanks!

Ideally, I'd like to see it go into solara, maybe a new component called FileUpload?

cheers,

Maarten

maartenbreddels avatar Oct 01 '24 19:10 maartenbreddels

I ended up implementing a DropOrInputFile{s} which combines logic from FileDrop and InputFile widgets to resolve all these points

What is InputFile? Is it an old solara component? Or do you mean solara.v.FileInput? In any case, I'm surprised there isn't a straight forward FileUpload in solara similar to ipywidgets.FileUpload. The closest I found is solara.v.FileInput (ipyvuetify), but it is very buggy!

@maartenbreddels are there plans to integrate a FileUpload component? I'd be happy to have a go at it. Could use it in my app (as do others - see Discord post). I think solara.v.FileInput is mostly there. Just need to resolve the bugs.

edan-bainglass avatar Jun 16 '25 16:06 edan-bainglass

Also, @maartenbreddels, I believe the documentation of FileDrop is off. For example, it suggests that on_file passes a FileInfo object. However,

solara.FileDrop(
    on_file=lambda f: print(type(f), f),
)

yields

<class 'dict'>
{
    'name': str, 
    'isFile': bool, 
    'size': int, 
    'file_obj': ipyvuetify.extra.file_input.ClientSideFile, 
    'data': ?
}

where here I'm only showing the types of what comes out. In any case, this is not even a dictionary form of FileInfo, as there is no FileInfo.isFile.

Am I missing something?

edan-bainglass avatar Jun 17 '25 09:06 edan-bainglass

Hi @edan-bainglass,

Calling type on the file returning dict makes sense - FileInfo is both typed as TypedDict: https://github.com/widgetti/solara/blob/a77ab17bfd142a79b80d4e61d3c4995d8557375f/solara/components/file_drop.py#L15-L19 and at runtime the instance is created as a regular dict as passed from the frontend.

The isFile property is added on the frontend (see file_drop.vue), but just not included in the type on the Python side for some reason.

iisakkirotko avatar Jun 17 '25 21:06 iisakkirotko

Ah, I missed that it was a TypedDict 👍

edan-bainglass avatar Jun 21 '25 09:06 edan-bainglass

I encountered a bug related to FileDropMultiple that has a label "Drop files here" and an on_total_progress to show file upload progress.

Once I drop file(s), the filename got stuck at the filedrop box, I implemented a clear-all function to clear/restore the component state to original state set_files_state([]) but not working, the filename did not disappear.

Is there any way to clear the filename from the filedrop box?

To reproduce, on_total_progress must be used in the FileDropMultiple function

HeWhoHeWho avatar Sep 09 '25 08:09 HeWhoHeWho