Expose on_drop
event trigger for rx.upload component. (#2766)
* Expose `on_drop` event trigger for rx.upload component. If `on_drop` is provided, it should be an EventSpec accepting the special rx.upload_files() arg. When `on_drop` is provided, it will be called immediately when files are selected. The default functionality of saving a file list to be uploaded later will not be available. * update pyi file * Undeprecate explicit EventChain
This commit is contained in:
parent
75b63cbc25
commit
c79719f7be
@ -374,21 +374,12 @@ class Component(BaseComponent, ABC):
|
|||||||
|
|
||||||
arg_spec = triggers.get(event_trigger, lambda: [])
|
arg_spec = triggers.get(event_trigger, lambda: [])
|
||||||
|
|
||||||
wrapped = False
|
|
||||||
# If the input is a single event handler, wrap it in a list.
|
# If the input is a single event handler, wrap it in a list.
|
||||||
if isinstance(value, (EventHandler, EventSpec)):
|
if isinstance(value, (EventHandler, EventSpec)):
|
||||||
wrapped = True
|
|
||||||
value = [value]
|
value = [value]
|
||||||
|
|
||||||
# If the input is a list of event handlers, create an event chain.
|
# If the input is a list of event handlers, create an event chain.
|
||||||
if isinstance(value, List):
|
if isinstance(value, List):
|
||||||
if not wrapped:
|
|
||||||
console.deprecate(
|
|
||||||
feature_name="EventChain",
|
|
||||||
reason="to avoid confusion, only use yield API",
|
|
||||||
deprecation_version="0.2.8",
|
|
||||||
removal_version="0.5.0",
|
|
||||||
)
|
|
||||||
events: list[EventSpec] = []
|
events: list[EventSpec] = []
|
||||||
for v in value:
|
for v in value:
|
||||||
if isinstance(v, EventHandler):
|
if isinstance(v, EventHandler):
|
||||||
|
@ -3,14 +3,21 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, ClassVar, Dict, List, Optional, Union
|
from typing import Any, Callable, ClassVar, Dict, List, Optional, Union
|
||||||
|
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.components.chakra.forms.input import Input
|
from reflex.components.chakra.forms.input import Input
|
||||||
from reflex.components.chakra.layout.box import Box
|
from reflex.components.chakra.layout.box import Box
|
||||||
from reflex.components.component import Component, MemoizationLeaf
|
from reflex.components.component import Component, MemoizationLeaf
|
||||||
from reflex.constants import Dirs
|
from reflex.constants import Dirs
|
||||||
from reflex.event import CallableEventSpec, EventChain, EventSpec, call_script
|
from reflex.event import (
|
||||||
|
CallableEventSpec,
|
||||||
|
EventChain,
|
||||||
|
EventSpec,
|
||||||
|
call_event_fn,
|
||||||
|
call_script,
|
||||||
|
parse_args_spec,
|
||||||
|
)
|
||||||
from reflex.utils import imports
|
from reflex.utils import imports
|
||||||
from reflex.vars import BaseVar, CallableVar, Var, VarData
|
from reflex.vars import BaseVar, CallableVar, Var, VarData
|
||||||
|
|
||||||
@ -135,6 +142,18 @@ def get_upload_url(file_path: str) -> str:
|
|||||||
return f"{uploaded_files_url_prefix}/{file_path}"
|
return f"{uploaded_files_url_prefix}/{file_path}"
|
||||||
|
|
||||||
|
|
||||||
|
def _on_drop_spec(files: Var):
|
||||||
|
"""Args spec for the on_drop event trigger.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
files: The files to upload.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Signature for on_drop handler including the files to upload.
|
||||||
|
"""
|
||||||
|
return [files]
|
||||||
|
|
||||||
|
|
||||||
class UploadFilesProvider(Component):
|
class UploadFilesProvider(Component):
|
||||||
"""AppWrap component that provides a dict of selected files by ID via useContext."""
|
"""AppWrap component that provides a dict of selected files by ID via useContext."""
|
||||||
|
|
||||||
@ -198,7 +217,7 @@ class Upload(MemoizationLeaf):
|
|||||||
cls.is_used = True
|
cls.is_used = True
|
||||||
|
|
||||||
# get only upload component props
|
# get only upload component props
|
||||||
supported_props = cls.get_props()
|
supported_props = cls.get_props().union({"on_drop"})
|
||||||
upload_props = {
|
upload_props = {
|
||||||
key: value for key, value in props.items() if key in supported_props
|
key: value for key, value in props.items() if key in supported_props
|
||||||
}
|
}
|
||||||
@ -218,8 +237,27 @@ class Upload(MemoizationLeaf):
|
|||||||
|
|
||||||
# Create the component.
|
# Create the component.
|
||||||
upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID)
|
upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID)
|
||||||
|
|
||||||
|
if upload_props.get("on_drop") is None:
|
||||||
|
# If on_drop is not provided, save files to be uploaded later.
|
||||||
|
upload_props["on_drop"] = upload_file(upload_props["id"])
|
||||||
|
else:
|
||||||
|
on_drop = upload_props["on_drop"]
|
||||||
|
if isinstance(on_drop, Callable):
|
||||||
|
# Call the lambda to get the event chain.
|
||||||
|
on_drop = call_event_fn(on_drop, _on_drop_spec) # type: ignore
|
||||||
|
if isinstance(on_drop, EventSpec):
|
||||||
|
# Update the provided args for direct use with on_drop.
|
||||||
|
on_drop = on_drop.with_args(
|
||||||
|
args=tuple(
|
||||||
|
cls._update_arg_tuple_for_on_drop(arg_value)
|
||||||
|
for arg_value in on_drop.args
|
||||||
|
),
|
||||||
|
)
|
||||||
|
upload_props["on_drop"] = on_drop
|
||||||
return super().create(
|
return super().create(
|
||||||
zone, on_drop=upload_file(upload_props["id"]), **upload_props
|
zone,
|
||||||
|
**upload_props,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
|
def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
|
||||||
@ -230,9 +268,24 @@ class Upload(MemoizationLeaf):
|
|||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
**super().get_event_triggers(),
|
**super().get_event_triggers(),
|
||||||
constants.EventTriggers.ON_DROP: lambda e0: [e0],
|
constants.EventTriggers.ON_DROP: _on_drop_spec,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _update_arg_tuple_for_on_drop(cls, arg_value: tuple[Var, Var]):
|
||||||
|
"""Helper to update caller-provided EventSpec args for direct use with on_drop.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
arg_value: The arg tuple to update (if necessary).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The updated arg_value tuple when arg is "files", otherwise the original arg_value.
|
||||||
|
"""
|
||||||
|
if arg_value[0]._var_name == "files":
|
||||||
|
placeholder = parse_args_spec(_on_drop_spec)[0]
|
||||||
|
return (arg_value[0], placeholder)
|
||||||
|
return arg_value
|
||||||
|
|
||||||
def _render(self):
|
def _render(self):
|
||||||
out = super()._render()
|
out = super()._render()
|
||||||
out.args = ("getRootProps", "getInputProps")
|
out.args = ("getRootProps", "getInputProps")
|
||||||
|
@ -9,13 +9,20 @@ from reflex.event import EventChain, EventHandler, EventSpec
|
|||||||
from reflex.style import Style
|
from reflex.style import Style
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, ClassVar, Dict, List, Optional, Union
|
from typing import Any, Callable, ClassVar, Dict, List, Optional, Union
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.components.chakra.forms.input import Input
|
from reflex.components.chakra.forms.input import Input
|
||||||
from reflex.components.chakra.layout.box import Box
|
from reflex.components.chakra.layout.box import Box
|
||||||
from reflex.components.component import Component, MemoizationLeaf
|
from reflex.components.component import Component, MemoizationLeaf
|
||||||
from reflex.constants import Dirs
|
from reflex.constants import Dirs
|
||||||
from reflex.event import CallableEventSpec, EventChain, EventSpec, call_script
|
from reflex.event import (
|
||||||
|
CallableEventSpec,
|
||||||
|
EventChain,
|
||||||
|
EventSpec,
|
||||||
|
call_event_fn,
|
||||||
|
call_script,
|
||||||
|
parse_args_spec,
|
||||||
|
)
|
||||||
from reflex.utils import imports
|
from reflex.utils import imports
|
||||||
from reflex.vars import BaseVar, CallableVar, Var, VarData
|
from reflex.vars import BaseVar, CallableVar, Var, VarData
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user