111 lines
3.4 KiB
Python
111 lines
3.4 KiB
Python
"""A file upload component."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Dict, List, Optional, Union
|
|
|
|
from reflex.components.component import Component
|
|
from reflex.components.forms.input import Input
|
|
from reflex.components.layout.box import Box
|
|
from reflex.constants import EventTriggers
|
|
from reflex.event import EventChain
|
|
from reflex.vars import BaseVar, Var
|
|
|
|
files_state: str = "const [files, setFiles] = useState([]);"
|
|
upload_file: BaseVar = BaseVar(name="e => setFiles((files) => e)", type_=EventChain)
|
|
|
|
# Use this var along with the Upload component to render the list of selected files.
|
|
selected_files: BaseVar = BaseVar(name="files.map((f) => f.name)", type_=List[str])
|
|
|
|
clear_selected_files: BaseVar = BaseVar(
|
|
name="_e => setFiles((files) => [])", type_=EventChain
|
|
)
|
|
|
|
|
|
class Upload(Component):
|
|
"""A file upload component."""
|
|
|
|
library = "react-dropzone@^14.2.3"
|
|
|
|
tag = "ReactDropzone"
|
|
|
|
is_default = True
|
|
|
|
# The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as
|
|
# values.
|
|
# supported MIME types: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
|
accept: Var[Optional[Dict[str, List]]]
|
|
|
|
# Whether the dropzone is disabled.
|
|
disabled: Var[bool]
|
|
|
|
# The maximum number of files that can be uploaded.
|
|
max_files: Var[int]
|
|
|
|
# The maximum file size (bytes) that can be uploaded.
|
|
max_size: Var[int]
|
|
|
|
# The minimum file size (bytes) that can be uploaded.
|
|
min_size: Var[int]
|
|
|
|
# Whether to allow multiple files to be uploaded.
|
|
multiple: Var[bool] = True # type: ignore
|
|
|
|
# Whether to disable click to upload.
|
|
no_click: Var[bool]
|
|
|
|
# Whether to disable drag and drop.
|
|
no_drag: Var[bool]
|
|
|
|
# Whether to disable using the space/enter keys to upload.
|
|
no_keyboard: Var[bool]
|
|
|
|
@classmethod
|
|
def create(cls, *children, **props) -> Component:
|
|
"""Create an upload component.
|
|
|
|
Args:
|
|
*children: The children of the component.
|
|
**props: The properties of the component.
|
|
|
|
Returns:
|
|
The upload component.
|
|
"""
|
|
# get only upload component props
|
|
supported_props = cls.get_props()
|
|
upload_props = {
|
|
key: value for key, value in props.items() if key in supported_props
|
|
}
|
|
# The file input to use.
|
|
upload = Input.create(type_="file")
|
|
upload.special_props = {BaseVar(name="{...getInputProps()}", type_=None)}
|
|
|
|
# The dropzone to use.
|
|
zone = Box.create(
|
|
upload,
|
|
*children,
|
|
**{k: v for k, v in props.items() if k not in supported_props},
|
|
)
|
|
zone.special_props = {BaseVar(name="{...getRootProps()}", type_=None)}
|
|
|
|
# Create the component.
|
|
return super().create(zone, on_drop=upload_file, **upload_props)
|
|
|
|
def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
|
|
"""Get the event triggers that pass the component's value to the handler.
|
|
|
|
Returns:
|
|
A dict mapping the event trigger to the var that is passed to the handler.
|
|
"""
|
|
return {
|
|
**super().get_event_triggers(),
|
|
EventTriggers.ON_DROP: lambda e0: [e0],
|
|
}
|
|
|
|
def _render(self):
|
|
out = super()._render()
|
|
out.args = ("getRootProps", "getInputProps")
|
|
return out
|
|
|
|
def _get_hooks(self) -> str | None:
|
|
return (super()._get_hooks() or "") + files_state
|