Skip to content
Open
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
63 changes: 51 additions & 12 deletions packages/reflex-base/src/reflex_base/components/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from reflex_base.components.field import BaseField, FieldBasedMeta
from reflex_base.components.tags import Tag
from reflex_base.constants import Dirs, EventTriggers, Hooks, Imports, MemoizationMode
from reflex_base.constants.colors import Color
from reflex_base.constants.compiler import SpecialAttributes
from reflex_base.constants.state import CAMEL_CASE_MEMO_MARKER
from reflex_base.event import (
Expand All @@ -40,7 +41,7 @@
run_script,
unwrap_var_annotation,
)
from reflex_base.style import Style, format_as_emotion
from reflex_base.style import Style, format_as_emotion, validate_literal_css_color_value
from reflex_base.utils import console, format, imports, types
from reflex_base.utils.imports import ImportDict, ImportVar, ParsedImportDict
from reflex_base.vars import VarData
Expand All @@ -64,6 +65,34 @@
import reflex.state

FIELD_TYPE = TypeVar("FIELD_TYPE")
_COLOR_VALIDATION_PROP_KEYS = frozenset({"fill", "stroke"})


def _is_color_validation_candidate_prop(prop_name: str) -> bool:
"""Check whether a prop name may carry color values.

Args:
prop_name: The component prop name.

Returns:
Whether color validation should be considered for this prop.
"""
prop_name = prop_name.lower()
return "color" in prop_name or prop_name in _COLOR_VALIDATION_PROP_KEYS


def _typehint_includes_color(type_hint: Any) -> bool:
"""Check whether a type hint contains the Reflex Color type.

Args:
type_hint: The type hint to inspect.

Returns:
Whether the type hint contains Color.
"""
if type_hint is Color:
return True
return any(_typehint_includes_color(arg) for arg in typing.get_args(type_hint))


class ComponentField(BaseField[FIELD_TYPE]):
Expand Down Expand Up @@ -923,20 +952,37 @@ def _post_init(self, *args, **kwargs):
else:
continue

if key in component_specific_triggers:
kwargs["event_triggers"][key] = EventChain.create(
value=value,
args_spec=component_specific_triggers[key],
key=key,
)
continue

# Check whether the key is a component prop.
if is_var:
field_type = types.get_field_type(type(self), key)
expected_var_type_args = typing.get_args(field_type)
expected_var_type = (
expected_var_type_args[0] if expected_var_type_args else field_type
)
if (
isinstance(value, str)
and _is_color_validation_candidate_prop(key)
and _typehint_includes_color(expected_var_type)
):
validate_literal_css_color_value(key, value)
try:
kwargs[key] = LiteralVar.create(value)

# Get the passed type and the var type.
passed_type = kwargs[key]._var_type
expected_type = typing.get_args(
types.get_field_type(type(self), key)
)[0]
expected_type = expected_var_type
except TypeError:
# If it is not a valid var, check the base types.
passed_type = type(value)
expected_type = types.get_field_type(type(self), key)
expected_type = field_type

if not satisfies_type_hint(value, expected_type):
value_name = value._js_expr if isinstance(value, Var) else value
Expand All @@ -951,13 +997,6 @@ def _post_init(self, *args, **kwargs):
f"Invalid var passed for prop {type(self).__name__}.{key}, expected type {expected_type}, got value {value_name} of type {passed_type}."
+ additional_info
)
# Check if the key is an event trigger.
if key in component_specific_triggers:
kwargs["event_triggers"][key] = EventChain.create(
value=value,
args_spec=component_specific_triggers[key],
key=key,
)

# Remove any keys that were added as events.
for key in kwargs["event_triggers"]:
Expand Down
Loading
Loading