Move _create_event_chain to EventChain.create (#4557)
The ability to convert `EventType` arguments into `EventChain` is crucial for wrapping JS libraries that need to pass event handlers via hooks or other non-component centric interfaces. Improve typing such that wrapped components accepting `EventType` arguments can be directly converted to `EventChain` / `EventChainVar` to be rendered as JS code.
This commit is contained in:
parent
848b87070c
commit
41ed9f0514
@ -23,6 +23,8 @@ from typing import (
|
|||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from typing_extensions import deprecated
|
||||||
|
|
||||||
import reflex.state
|
import reflex.state
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.compiler.templates import STATEFUL_COMPONENT
|
from reflex.compiler.templates import STATEFUL_COMPONENT
|
||||||
@ -43,17 +45,13 @@ from reflex.constants.state import FRONTEND_EVENT_STATE
|
|||||||
from reflex.event import (
|
from reflex.event import (
|
||||||
EventCallback,
|
EventCallback,
|
||||||
EventChain,
|
EventChain,
|
||||||
EventChainVar,
|
|
||||||
EventHandler,
|
EventHandler,
|
||||||
EventSpec,
|
EventSpec,
|
||||||
EventVar,
|
EventVar,
|
||||||
call_event_fn,
|
|
||||||
call_event_handler,
|
|
||||||
get_handler_args,
|
|
||||||
no_args_event_spec,
|
no_args_event_spec,
|
||||||
)
|
)
|
||||||
from reflex.style import Style, format_as_emotion
|
from reflex.style import Style, format_as_emotion
|
||||||
from reflex.utils import format, imports, types
|
from reflex.utils import console, format, imports, types
|
||||||
from reflex.utils.imports import (
|
from reflex.utils.imports import (
|
||||||
ImmutableParsedImportDict,
|
ImmutableParsedImportDict,
|
||||||
ImportDict,
|
ImportDict,
|
||||||
@ -493,8 +491,7 @@ class Component(BaseComponent, ABC):
|
|||||||
)
|
)
|
||||||
# Check if the key is an event trigger.
|
# Check if the key is an event trigger.
|
||||||
if key in component_specific_triggers:
|
if key in component_specific_triggers:
|
||||||
# Temporarily disable full control for event triggers.
|
kwargs["event_triggers"][key] = EventChain.create(
|
||||||
kwargs["event_triggers"][key] = self._create_event_chain(
|
|
||||||
value=value, # type: ignore
|
value=value, # type: ignore
|
||||||
args_spec=component_specific_triggers[key],
|
args_spec=component_specific_triggers[key],
|
||||||
key=key,
|
key=key,
|
||||||
@ -548,6 +545,7 @@ class Component(BaseComponent, ABC):
|
|||||||
# Construct the component.
|
# Construct the component.
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@deprecated("Use rx.EventChain.create instead.")
|
||||||
def _create_event_chain(
|
def _create_event_chain(
|
||||||
self,
|
self,
|
||||||
args_spec: types.ArgsSpec | Sequence[types.ArgsSpec],
|
args_spec: types.ArgsSpec | Sequence[types.ArgsSpec],
|
||||||
@ -569,82 +567,18 @@ class Component(BaseComponent, ABC):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The event chain.
|
The event chain.
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If the value is not a valid event chain.
|
|
||||||
"""
|
"""
|
||||||
# If it's an event chain var, return it.
|
console.deprecate(
|
||||||
if isinstance(value, Var):
|
"Component._create_event_chain",
|
||||||
if isinstance(value, EventChainVar):
|
"Use rx.EventChain.create instead.",
|
||||||
return value
|
deprecation_version="0.6.8",
|
||||||
elif isinstance(value, EventVar):
|
removal_version="0.7.0",
|
||||||
value = [value]
|
)
|
||||||
elif issubclass(value._var_type, (EventChain, EventSpec)):
|
return EventChain.create(
|
||||||
return self._create_event_chain(args_spec, value.guess_type(), key=key)
|
value=value, # type: ignore
|
||||||
else:
|
args_spec=args_spec,
|
||||||
raise ValueError(
|
key=key,
|
||||||
f"Invalid event chain: {value!s} of type {value._var_type}"
|
)
|
||||||
)
|
|
||||||
elif isinstance(value, EventChain):
|
|
||||||
# Trust that the caller knows what they're doing passing an EventChain directly
|
|
||||||
return value
|
|
||||||
|
|
||||||
# If the input is a single event handler, wrap it in a list.
|
|
||||||
if isinstance(value, (EventHandler, EventSpec)):
|
|
||||||
value = [value]
|
|
||||||
|
|
||||||
# If the input is a list of event handlers, create an event chain.
|
|
||||||
if isinstance(value, List):
|
|
||||||
events: List[Union[EventSpec, EventVar]] = []
|
|
||||||
for v in value:
|
|
||||||
if isinstance(v, (EventHandler, EventSpec)):
|
|
||||||
# Call the event handler to get the event.
|
|
||||||
events.append(call_event_handler(v, args_spec, key=key))
|
|
||||||
elif isinstance(v, Callable):
|
|
||||||
# Call the lambda to get the event chain.
|
|
||||||
result = call_event_fn(v, args_spec, key=key)
|
|
||||||
if isinstance(result, Var):
|
|
||||||
raise ValueError(
|
|
||||||
f"Invalid event chain: {v}. Cannot use a Var-returning "
|
|
||||||
"lambda inside an EventChain list."
|
|
||||||
)
|
|
||||||
events.extend(result)
|
|
||||||
elif isinstance(v, EventVar):
|
|
||||||
events.append(v)
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Invalid event: {v}")
|
|
||||||
|
|
||||||
# If the input is a callable, create an event chain.
|
|
||||||
elif isinstance(value, Callable):
|
|
||||||
result = call_event_fn(value, args_spec, key=key)
|
|
||||||
if isinstance(result, Var):
|
|
||||||
# Recursively call this function if the lambda returned an EventChain Var.
|
|
||||||
return self._create_event_chain(args_spec, result, key=key)
|
|
||||||
events = [*result]
|
|
||||||
|
|
||||||
# Otherwise, raise an error.
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Invalid event chain: {value}")
|
|
||||||
|
|
||||||
# Add args to the event specs if necessary.
|
|
||||||
events = [
|
|
||||||
(e.with_args(get_handler_args(e)) if isinstance(e, EventSpec) else e)
|
|
||||||
for e in events
|
|
||||||
]
|
|
||||||
|
|
||||||
# Return the event chain.
|
|
||||||
if isinstance(args_spec, Var):
|
|
||||||
return EventChain(
|
|
||||||
events=events,
|
|
||||||
args_spec=None,
|
|
||||||
event_actions={},
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return EventChain(
|
|
||||||
events=events,
|
|
||||||
args_spec=args_spec,
|
|
||||||
event_actions={},
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_event_triggers(
|
def get_event_triggers(
|
||||||
self,
|
self,
|
||||||
@ -1737,7 +1671,7 @@ class CustomComponent(Component):
|
|||||||
|
|
||||||
# Handle event chains.
|
# Handle event chains.
|
||||||
if types._issubclass(type_, EventChain):
|
if types._issubclass(type_, EventChain):
|
||||||
value = self._create_event_chain(
|
value = EventChain.create(
|
||||||
value=value,
|
value=value,
|
||||||
args_spec=event_triggers_in_component_declaration.get(
|
args_spec=event_triggers_in_component_declaration.get(
|
||||||
key, no_args_event_spec
|
key, no_args_event_spec
|
||||||
|
@ -431,6 +431,96 @@ class EventChain(EventActionsMixin):
|
|||||||
|
|
||||||
invocation: Optional[Var] = dataclasses.field(default=None)
|
invocation: Optional[Var] = dataclasses.field(default=None)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(
|
||||||
|
cls,
|
||||||
|
value: EventType,
|
||||||
|
args_spec: ArgsSpec | Sequence[ArgsSpec],
|
||||||
|
key: Optional[str] = None,
|
||||||
|
) -> Union[EventChain, Var]:
|
||||||
|
"""Create an event chain from a variety of input types.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The value to create the event chain from.
|
||||||
|
args_spec: The args_spec of the event trigger being bound.
|
||||||
|
key: The key of the event trigger being bound.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The event chain.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If the value is not a valid event chain.
|
||||||
|
"""
|
||||||
|
# If it's an event chain var, return it.
|
||||||
|
if isinstance(value, Var):
|
||||||
|
if isinstance(value, EventChainVar):
|
||||||
|
return value
|
||||||
|
elif isinstance(value, EventVar):
|
||||||
|
value = [value]
|
||||||
|
elif issubclass(value._var_type, (EventChain, EventSpec)):
|
||||||
|
return cls.create(
|
||||||
|
value=value.guess_type(),
|
||||||
|
args_spec=args_spec,
|
||||||
|
key=key,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid event chain: {value!s} of type {value._var_type}"
|
||||||
|
)
|
||||||
|
elif isinstance(value, EventChain):
|
||||||
|
# Trust that the caller knows what they're doing passing an EventChain directly
|
||||||
|
return value
|
||||||
|
|
||||||
|
# If the input is a single event handler, wrap it in a list.
|
||||||
|
if isinstance(value, (EventHandler, EventSpec)):
|
||||||
|
value = [value]
|
||||||
|
|
||||||
|
# If the input is a list of event handlers, create an event chain.
|
||||||
|
if isinstance(value, List):
|
||||||
|
events: List[Union[EventSpec, EventVar]] = []
|
||||||
|
for v in value:
|
||||||
|
if isinstance(v, (EventHandler, EventSpec)):
|
||||||
|
# Call the event handler to get the event.
|
||||||
|
events.append(call_event_handler(v, args_spec, key=key))
|
||||||
|
elif isinstance(v, Callable):
|
||||||
|
# Call the lambda to get the event chain.
|
||||||
|
result = call_event_fn(v, args_spec, key=key)
|
||||||
|
if isinstance(result, Var):
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid event chain: {v}. Cannot use a Var-returning "
|
||||||
|
"lambda inside an EventChain list."
|
||||||
|
)
|
||||||
|
events.extend(result)
|
||||||
|
elif isinstance(v, EventVar):
|
||||||
|
events.append(v)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Invalid event: {v}")
|
||||||
|
|
||||||
|
# If the input is a callable, create an event chain.
|
||||||
|
elif isinstance(value, Callable):
|
||||||
|
result = call_event_fn(value, args_spec, key=key)
|
||||||
|
if isinstance(result, Var):
|
||||||
|
# Recursively call this function if the lambda returned an EventChain Var.
|
||||||
|
return cls.create(value=result, args_spec=args_spec, key=key)
|
||||||
|
events = [*result]
|
||||||
|
|
||||||
|
# Otherwise, raise an error.
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Invalid event chain: {value}")
|
||||||
|
|
||||||
|
# Add args to the event specs if necessary.
|
||||||
|
events = [
|
||||||
|
(e.with_args(get_handler_args(e)) if isinstance(e, EventSpec) else e)
|
||||||
|
for e in events
|
||||||
|
]
|
||||||
|
|
||||||
|
# Return the event chain.
|
||||||
|
return cls(
|
||||||
|
events=events,
|
||||||
|
args_spec=args_spec,
|
||||||
|
event_actions={},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
@dataclasses.dataclass(
|
||||||
init=True,
|
init=True,
|
||||||
|
Loading…
Reference in New Issue
Block a user