Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyi_hashes.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"reflex/components/core/helmet.pyi": "43f8497c8fafe51e29dca1dd535d143a",
"reflex/components/core/html.pyi": "ea5919db8c8172913185977df900f36b",
"reflex/components/core/sticky.pyi": "a9b4492e423f1dd4ccbf270c8ea90157",
"reflex/components/core/upload.pyi": "360fb929edf960aca289a37d0433fc38",
"reflex/components/core/upload.pyi": "77e828bbc55dd6593efdba1504e0cb5e",
"reflex/components/core/window_events.pyi": "76bf03a273a1fbbb3b333e10d5d08c30",
"reflex/components/datadisplay/__init__.pyi": "52755871369acbfd3a96b46b9a11d32e",
"reflex/components/datadisplay/code.pyi": "b86769987ef4d1cbdddb461be88539fd",
Expand Down
67 changes: 53 additions & 14 deletions reflex/components/core/upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from reflex.components.core.cond import cond
from reflex.components.el.elements.forms import Input
from reflex.components.radix.themes.layout.box import Box
from reflex.components.sonner.toast import toast
from reflex.constants import Dirs
from reflex.constants.compiler import Hooks, Imports
from reflex.environment import environment
Expand All @@ -36,7 +37,8 @@
from reflex.vars import VarData
from reflex.vars.base import Var, get_unique_variable_name
from reflex.vars.function import FunctionVar
from reflex.vars.sequence import LiteralStringVar
from reflex.vars.object import ObjectVar
from reflex.vars.sequence import ArrayVar, LiteralStringVar

DEFAULT_UPLOAD_ID: str = "default"

Expand Down Expand Up @@ -178,6 +180,34 @@ def _on_drop_spec(files: Var) -> tuple[Var[Any]]:
return (files,)


def _default_drop_rejected(rejected_files: ArrayVar[list[dict[str, Any]]]) -> EventSpec:
"""Event handler for showing a toast with rejected file info.

Args:
rejected_files: The files that were rejected.

Returns:
An event spec that shows a toast with the rejected file info when triggered.
"""

def _format_rejected_file_record(rf: ObjectVar[dict[str, Any]]) -> str:
rf = rf.to(ObjectVar, dict[str, dict[str, Any]])
file = rf["file"].to(ObjectVar, dict[str, Any])
errors = rf["errors"].to(ArrayVar, list[dict[str, Any]])
return (
f"{file['path']}: {errors.foreach(lambda err: err['message']).join(', ')}"
)

return toast.error(
title="Files not Accepted",
description=rejected_files.to(ArrayVar)
.foreach(_format_rejected_file_record)
.join("\n\n"),
close_button=True,
style={"white_space": "pre-line"},
)


class UploadFilesProvider(Component):
"""AppWrap component that provides a dict of selected files by ID via useContext."""

Expand All @@ -191,6 +221,9 @@ class GhostUpload(Fragment):
# Fired when files are dropped.
on_drop: EventHandler[_on_drop_spec]

# Fired when dropped files do not meet the specified criteria.
on_drop_rejected: EventHandler[_on_drop_spec]


class Upload(MemoizationLeaf):
"""A file upload component."""
Expand Down Expand Up @@ -234,6 +267,9 @@ class Upload(MemoizationLeaf):
# Fired when files are dropped.
on_drop: EventHandler[_on_drop_spec]

# Fired when dropped files do not meet the specified criteria.
on_drop_rejected: EventHandler[_on_drop_spec]

# Style rules to apply when actively dragging.
drag_active_style: Style | None = field(default=None, is_javascript_property=False)

Expand Down Expand Up @@ -295,6 +331,10 @@ def create(cls, *children, **props) -> Component:
on_drop[ix] = event
upload_props["on_drop"] = on_drop

if upload_props.get("on_drop_rejected") is None:
# If on_drop_rejected is not provided, show an error toast.
upload_props["on_drop_rejected"] = _default_drop_rejected

input_props_unique_name = get_unique_variable_name()
root_props_unique_name = get_unique_variable_name()
is_drag_active_unique_name = get_unique_variable_name()
Expand All @@ -313,22 +353,22 @@ def create(cls, *children, **props) -> Component:
),
)

event_var, callback_str = StatefulComponent._get_memoized_event_triggers(
GhostUpload.create(on_drop=upload_props["on_drop"])
)["on_drop"]

upload_props["on_drop"] = event_var
event_triggers = StatefulComponent._get_memoized_event_triggers(
GhostUpload.create(
on_drop=upload_props["on_drop"],
on_drop_rejected=upload_props["on_drop_rejected"],
)
)
callback_hooks = []
for trigger_name, (event_var, callback_str) in event_triggers.items():
upload_props[trigger_name] = event_var
callback_hooks.append(callback_str)

upload_props = {
format.to_camel_case(key): value for key, value in upload_props.items()
}

use_dropzone_arguments = Var.create(
{
"onDrop": event_var,
**upload_props,
}
)
use_dropzone_arguments = Var.create(upload_props)

left_side = (
"const { "
Expand All @@ -344,11 +384,10 @@ def create(cls, *children, **props) -> Component:
imports=Imports.EVENTS,
hooks={Hooks.EVENTS: None},
),
event_var._get_all_var_data(),
use_dropzone_arguments._get_all_var_data(),
VarData(
hooks={
callback_str: None,
**dict.fromkeys(callback_hooks, None),
f"{left_side} = {right_side};": None,
},
imports={
Expand Down
Loading