|
@@ -19,13 +19,12 @@ from typing import (
|
|
Sequence,
|
|
Sequence,
|
|
Set,
|
|
Set,
|
|
Type,
|
|
Type,
|
|
|
|
+ TypeVar,
|
|
Union,
|
|
Union,
|
|
get_args,
|
|
get_args,
|
|
get_origin,
|
|
get_origin,
|
|
)
|
|
)
|
|
|
|
|
|
-from typing_extensions import Self
|
|
|
|
-
|
|
|
|
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
|
|
@@ -210,6 +209,27 @@ def _components_from(
|
|
return ()
|
|
return ()
|
|
|
|
|
|
|
|
|
|
|
|
+DEFAULT_TRIGGERS: dict[str, types.ArgsSpec | Sequence[types.ArgsSpec]] = {
|
|
|
|
+ EventTriggers.ON_FOCUS: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_BLUR: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_CLICK: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_CONTEXT_MENU: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_DOUBLE_CLICK: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_MOUSE_DOWN: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_MOUSE_ENTER: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_MOUSE_LEAVE: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_MOUSE_MOVE: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_MOUSE_OUT: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_MOUSE_OVER: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_MOUSE_UP: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_SCROLL: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_MOUNT: no_args_event_spec,
|
|
|
|
+ EventTriggers.ON_UNMOUNT: no_args_event_spec,
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+T = TypeVar("T", bound="Component")
|
|
|
|
+
|
|
|
|
+
|
|
class Component(BaseComponent, ABC):
|
|
class Component(BaseComponent, ABC):
|
|
"""A component with style, event trigger and other props."""
|
|
"""A component with style, event trigger and other props."""
|
|
|
|
|
|
@@ -364,12 +384,16 @@ class Component(BaseComponent, ABC):
|
|
if field.name not in props:
|
|
if field.name not in props:
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
+ field_type = types.value_inside_optional(
|
|
|
|
+ types.get_field_type(cls, field.name)
|
|
|
|
+ )
|
|
|
|
+
|
|
# Set default values for any props.
|
|
# Set default values for any props.
|
|
- if types._issubclass(field.type_, Var):
|
|
|
|
|
|
+ if types._issubclass(field_type, Var):
|
|
field.required = False
|
|
field.required = False
|
|
if field.default is not None:
|
|
if field.default is not None:
|
|
field.default = LiteralVar.create(field.default)
|
|
field.default = LiteralVar.create(field.default)
|
|
- elif types._issubclass(field.type_, EventHandler):
|
|
|
|
|
|
+ elif types._issubclass(field_type, EventHandler):
|
|
field.required = False
|
|
field.required = False
|
|
|
|
|
|
# Ensure renamed props from parent classes are applied to the subclass.
|
|
# Ensure renamed props from parent classes are applied to the subclass.
|
|
@@ -380,7 +404,7 @@ class Component(BaseComponent, ABC):
|
|
inherited_rename_props.update(parent._rename_props)
|
|
inherited_rename_props.update(parent._rename_props)
|
|
cls._rename_props = inherited_rename_props
|
|
cls._rename_props = inherited_rename_props
|
|
|
|
|
|
- def __init__(self, *args, **kwargs):
|
|
|
|
|
|
+ def _post_init(self, *args, **kwargs):
|
|
"""Initialize the component.
|
|
"""Initialize the component.
|
|
|
|
|
|
Args:
|
|
Args:
|
|
@@ -393,16 +417,6 @@ class Component(BaseComponent, ABC):
|
|
"""
|
|
"""
|
|
# Set the id and children initially.
|
|
# Set the id and children initially.
|
|
children = kwargs.get("children", [])
|
|
children = kwargs.get("children", [])
|
|
- initial_kwargs = {
|
|
|
|
- "id": kwargs.get("id"),
|
|
|
|
- "children": children,
|
|
|
|
- **{
|
|
|
|
- prop: LiteralVar.create(kwargs[prop])
|
|
|
|
- for prop in self.get_initial_props()
|
|
|
|
- if prop in kwargs
|
|
|
|
- },
|
|
|
|
- }
|
|
|
|
- super().__init__(**initial_kwargs)
|
|
|
|
|
|
|
|
self._validate_component_children(children)
|
|
self._validate_component_children(children)
|
|
|
|
|
|
@@ -433,7 +447,9 @@ class Component(BaseComponent, ABC):
|
|
field_type = EventChain
|
|
field_type = EventChain
|
|
elif key in props:
|
|
elif key in props:
|
|
# Set the field type.
|
|
# Set the field type.
|
|
- field_type = fields[key].type_
|
|
|
|
|
|
+ field_type = types.value_inside_optional(
|
|
|
|
+ types.get_field_type(type(self), key)
|
|
|
|
+ )
|
|
|
|
|
|
else:
|
|
else:
|
|
continue
|
|
continue
|
|
@@ -455,7 +471,10 @@ class Component(BaseComponent, ABC):
|
|
try:
|
|
try:
|
|
kwargs[key] = determine_key(value)
|
|
kwargs[key] = determine_key(value)
|
|
|
|
|
|
- expected_type = fields[key].outer_type_.__args__[0]
|
|
|
|
|
|
+ expected_type = types.get_args(
|
|
|
|
+ types.get_field_type(type(self), key)
|
|
|
|
+ )[0]
|
|
|
|
+
|
|
# validate literal fields.
|
|
# validate literal fields.
|
|
types.validate_literal(
|
|
types.validate_literal(
|
|
key, value, expected_type, type(self).__name__
|
|
key, value, expected_type, type(self).__name__
|
|
@@ -470,7 +489,7 @@ class Component(BaseComponent, ABC):
|
|
except TypeError:
|
|
except TypeError:
|
|
# If it is not a valid var, check the base types.
|
|
# If it is not a valid var, check the base types.
|
|
passed_type = type(value)
|
|
passed_type = type(value)
|
|
- expected_type = fields[key].outer_type_
|
|
|
|
|
|
+ expected_type = types.get_field_type(type(self), key)
|
|
if types.is_union(passed_type):
|
|
if types.is_union(passed_type):
|
|
# We need to check all possible types in the union.
|
|
# We need to check all possible types in the union.
|
|
passed_types = (
|
|
passed_types = (
|
|
@@ -552,7 +571,8 @@ class Component(BaseComponent, ABC):
|
|
kwargs["class_name"] = " ".join(class_name)
|
|
kwargs["class_name"] = " ".join(class_name)
|
|
|
|
|
|
# Construct the component.
|
|
# Construct the component.
|
|
- super().__init__(*args, **kwargs)
|
|
|
|
|
|
+ for key, value in kwargs.items():
|
|
|
|
+ setattr(self, key, value)
|
|
|
|
|
|
def get_event_triggers(
|
|
def get_event_triggers(
|
|
self,
|
|
self,
|
|
@@ -562,34 +582,17 @@ class Component(BaseComponent, ABC):
|
|
Returns:
|
|
Returns:
|
|
The event triggers.
|
|
The event triggers.
|
|
"""
|
|
"""
|
|
- default_triggers: dict[str, types.ArgsSpec | Sequence[types.ArgsSpec]] = {
|
|
|
|
- EventTriggers.ON_FOCUS: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_BLUR: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_CLICK: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_CONTEXT_MENU: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_DOUBLE_CLICK: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_MOUSE_DOWN: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_MOUSE_ENTER: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_MOUSE_LEAVE: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_MOUSE_MOVE: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_MOUSE_OUT: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_MOUSE_OVER: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_MOUSE_UP: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_SCROLL: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_MOUNT: no_args_event_spec,
|
|
|
|
- EventTriggers.ON_UNMOUNT: no_args_event_spec,
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ triggers = DEFAULT_TRIGGERS.copy()
|
|
# Look for component specific triggers,
|
|
# Look for component specific triggers,
|
|
# e.g. variable declared as EventHandler types.
|
|
# e.g. variable declared as EventHandler types.
|
|
for field in self.get_fields().values():
|
|
for field in self.get_fields().values():
|
|
- if types._issubclass(field.outer_type_, EventHandler):
|
|
|
|
|
|
+ if field.type_ is EventHandler:
|
|
args_spec = None
|
|
args_spec = None
|
|
annotation = field.annotation
|
|
annotation = field.annotation
|
|
if (metadata := getattr(annotation, "__metadata__", None)) is not None:
|
|
if (metadata := getattr(annotation, "__metadata__", None)) is not None:
|
|
args_spec = metadata[0]
|
|
args_spec = metadata[0]
|
|
- default_triggers[field.name] = args_spec or (no_args_event_spec)
|
|
|
|
- return default_triggers
|
|
|
|
|
|
+ triggers[field.name] = args_spec or (no_args_event_spec)
|
|
|
|
+ return triggers
|
|
|
|
|
|
def __repr__(self) -> str:
|
|
def __repr__(self) -> str:
|
|
"""Represent the component in React.
|
|
"""Represent the component in React.
|
|
@@ -703,9 +706,11 @@ class Component(BaseComponent, ABC):
|
|
"""
|
|
"""
|
|
return {
|
|
return {
|
|
name
|
|
name
|
|
- for name, field in cls.get_fields().items()
|
|
|
|
|
|
+ for name in cls.get_fields()
|
|
if name in cls.get_props()
|
|
if name in cls.get_props()
|
|
- and types._issubclass(field.outer_type_, Component)
|
|
|
|
|
|
+ and types._issubclass(
|
|
|
|
+ types.value_inside_optional(types.get_field_type(cls, name)), Component
|
|
|
|
+ )
|
|
}
|
|
}
|
|
|
|
|
|
def _get_components_in_props(self) -> Sequence[BaseComponent]:
|
|
def _get_components_in_props(self) -> Sequence[BaseComponent]:
|
|
@@ -729,7 +734,7 @@ class Component(BaseComponent, ABC):
|
|
]
|
|
]
|
|
|
|
|
|
@classmethod
|
|
@classmethod
|
|
- def create(cls, *children, **props) -> Self:
|
|
|
|
|
|
+ def create(cls: Type[T], *children, **props) -> T:
|
|
"""Create the component.
|
|
"""Create the component.
|
|
|
|
|
|
Args:
|
|
Args:
|
|
@@ -774,7 +779,22 @@ class Component(BaseComponent, ABC):
|
|
for child in children
|
|
for child in children
|
|
]
|
|
]
|
|
|
|
|
|
- return cls(children=children, **props)
|
|
|
|
|
|
+ return cls._create(children, **props)
|
|
|
|
+
|
|
|
|
+ @classmethod
|
|
|
|
+ def _create(cls: Type[T], children: list[Component], **props: Any) -> T:
|
|
|
|
+ """Create the component.
|
|
|
|
+
|
|
|
|
+ Args:
|
|
|
|
+ children: The children of the component.
|
|
|
|
+ **props: The props of the component.
|
|
|
|
+
|
|
|
|
+ Returns:
|
|
|
|
+ The component.
|
|
|
|
+ """
|
|
|
|
+ comp = cls.construct(id=props.get("id"), children=children)
|
|
|
|
+ comp._post_init(children=children, **props)
|
|
|
|
+ return comp
|
|
|
|
|
|
def add_style(self) -> dict[str, Any] | None:
|
|
def add_style(self) -> dict[str, Any] | None:
|
|
"""Add style to the component.
|
|
"""Add style to the component.
|
|
@@ -1659,7 +1679,7 @@ class CustomComponent(Component):
|
|
# The props of the component.
|
|
# The props of the component.
|
|
props: dict[str, Any] = {}
|
|
props: dict[str, Any] = {}
|
|
|
|
|
|
- def __init__(self, **kwargs):
|
|
|
|
|
|
+ def _post_init(self, **kwargs):
|
|
"""Initialize the custom component.
|
|
"""Initialize the custom component.
|
|
|
|
|
|
Args:
|
|
Args:
|
|
@@ -1702,7 +1722,7 @@ class CustomComponent(Component):
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
- super().__init__(
|
|
|
|
|
|
+ super()._post_init(
|
|
event_triggers={
|
|
event_triggers={
|
|
key: EventChain.create(
|
|
key: EventChain.create(
|
|
value=props[key],
|
|
value=props[key],
|
|
@@ -1863,7 +1883,9 @@ def custom_component(
|
|
def wrapper(*children, **props) -> CustomComponent:
|
|
def wrapper(*children, **props) -> CustomComponent:
|
|
# Remove the children from the props.
|
|
# Remove the children from the props.
|
|
props.pop("children", None)
|
|
props.pop("children", None)
|
|
- return CustomComponent(component_fn=component_fn, children=children, **props)
|
|
|
|
|
|
+ return CustomComponent._create(
|
|
|
|
+ children=list(children), component_fn=component_fn, **props
|
|
|
|
+ )
|
|
|
|
|
|
return wrapper
|
|
return wrapper
|
|
|
|
|