diff --git a/reflex/components/component.py b/reflex/components/component.py index 008cae5f42e..e1ee54c91dd 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -58,7 +58,7 @@ ) from reflex.vars.function import ArgsFunctionOperation, FunctionStringVar, FunctionVar from reflex.vars.number import ternary_operation -from reflex.vars.object import LiteralObjectVar, ObjectVar +from reflex.vars.object import ObjectVar from reflex.vars.sequence import LiteralArrayVar, LiteralStringVar, StringVar @@ -500,36 +500,16 @@ def _post_init(self, *args, **kwargs): else: continue - def determine_key(value: Any): - # Try to create a var from the value - key = value if isinstance(value, Var) else LiteralVar.create(value) - - # Check that the var type is not None. - if key is None: - raise TypeError - - return key - # Check whether the key is a component prop. if is_var: try: - kwargs[key] = determine_key(value) + kwargs[key] = LiteralVar.create(value) + # Get the passed type and the var type. + passed_type = kwargs[key]._var_type expected_type = types.get_args( types.get_field_type(type(self), key) )[0] - - # validate literal fields. - types.validate_literal( - key, value, expected_type, type(self).__name__ - ) - # Get the passed type and the var type. - passed_type = kwargs[key]._var_type - expected_type = ( - type(expected_type.__args__[0]) - if types.is_literal(expected_type) - else expected_type - ) except TypeError: # If it is not a valid var, check the base types. passed_type = type(value) @@ -561,15 +541,19 @@ def determine_key(value: Any): kwargs.pop(key, None) # Place data_ and aria_ attributes into custom_attrs - special_attributes = tuple( + special_attributes = [ key for key in kwargs if key not in fields and SpecialAttributes.is_special(key) - ) + ] if special_attributes: custom_attrs = kwargs.setdefault("custom_attrs", {}) - for key in special_attributes: - custom_attrs[format.to_kebab_case(key)] = kwargs.pop(key) + custom_attrs.update( + { + format.to_kebab_case(key): kwargs.pop(key) + for key in special_attributes + } + ) # Add style props to the component. style = kwargs.get("style", {}) @@ -805,6 +789,18 @@ def _get_components_in_props(self) -> Sequence[BaseComponent]: for component in _components_from(value) ] + @classmethod + def _validate_children(cls, children: tuple | list): + from reflex.utils.exceptions import ChildrenTypeError + + for child in children: + if isinstance(child, (tuple, list)): + cls._validate_children(child) + + # Make sure the child is a valid type. + if isinstance(child, dict) or not isinstance(child, ComponentChildTypes): + raise ChildrenTypeError(component=cls.__name__, child=child) + @classmethod def create(cls: type[T], *children, **props) -> T: """Create the component. @@ -819,24 +815,12 @@ def create(cls: type[T], *children, **props) -> T: # Import here to avoid circular imports. from reflex.components.base.bare import Bare from reflex.components.base.fragment import Fragment - from reflex.utils.exceptions import ChildrenTypeError # Filter out None props props = {key: value for key, value in props.items() if value is not None} - def validate_children(children: tuple | list): - for child in children: - if isinstance(child, (tuple, list)): - validate_children(child) - - # Make sure the child is a valid type. - if isinstance(child, dict) or not isinstance( - child, ComponentChildTypes - ): - raise ChildrenTypeError(component=cls.__name__, child=child) - # Validate all the children. - validate_children(children) + cls._validate_children(children) children_normalized = [ ( @@ -2577,25 +2561,7 @@ def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) -> else LiteralNoneVar.create(), ) - props = {} - - special_props = [] - - for prop_str in tag["props"]: - if ":" not in prop_str: - special_props.append(Var(prop_str).to(ObjectVar)) - continue - prop = prop_str.index(":") - key = prop_str[:prop] - value = prop_str[prop + 1 :] - props[key] = value - - props = LiteralObjectVar.create( - {LiteralStringVar.create(k): Var(v) for k, v in props.items()} - ) - - for prop in special_props: - props = props.merge(prop) + props = Var("({" + ",".join(tag["props"]) + "})") contents = tag["contents"] if tag["contents"] else None diff --git a/reflex/constants/compiler.py b/reflex/constants/compiler.py index 3daa729e17f..3a59c631221 100644 --- a/reflex/constants/compiler.py +++ b/reflex/constants/compiler.py @@ -171,6 +171,12 @@ class MemoizationMode: recursive: bool = True +DATA_UNDERSCORE = "data_" +DATA_DASH = "data-" +ARIA_UNDERSCORE = "aria_" +ARIA_DASH = "aria-" + + class SpecialAttributes(enum.Enum): """Special attributes for components. @@ -178,11 +184,6 @@ class SpecialAttributes(enum.Enum): to a style prop. """ - DATA_UNDERSCORE = "data_" - DATA_DASH = "data-" - ARIA_UNDERSCORE = "aria_" - ARIA_DASH = "aria-" - @classmethod def is_special(cls, attr: str) -> bool: """Check if the attribute is special. @@ -193,4 +194,9 @@ def is_special(cls, attr: str) -> bool: Returns: True if the attribute is special. """ - return any(attr.startswith(value.value) for value in cls) + return ( + attr.startswith(DATA_UNDERSCORE) + or attr.startswith(DATA_DASH) + or attr.startswith(ARIA_UNDERSCORE) + or attr.startswith(ARIA_DASH) + ) diff --git a/reflex/utils/format.py b/reflex/utils/format.py index 54c404f1124..c5b04d5eb30 100644 --- a/reflex/utils/format.py +++ b/reflex/utils/format.py @@ -436,19 +436,12 @@ def format_props(*single_props, **key_value_props) -> list[str]: The formatted props list. """ # Format all the props. - from reflex.vars.base import LiteralVar, Var + from reflex.vars import LiteralStringVar, LiteralVar, Var return [ - ":".join( - [ - str(name if "-" not in name else LiteralVar.create(name)), - str( - format_prop( - prop if isinstance(prop, Var) else LiteralVar.create(prop) - ) - ), - ] - ) + (str(LiteralStringVar.create(name)) if "-" in name else name) + + ":" + + str(format_prop(prop if isinstance(prop, Var) else LiteralVar.create(prop))) for name, prop in sorted(key_value_props.items()) if prop is not None ] + [(f"...{LiteralVar.create(prop)!s}") for prop in single_props] diff --git a/tests/units/components/test_component.py b/tests/units/components/test_component.py index 2a994393493..d456cf0556f 100644 --- a/tests/units/components/test_component.py +++ b/tests/units/components/test_component.py @@ -1221,10 +1221,10 @@ def test_stateful_banner(): assert isinstance(stateful_component, StatefulComponent) -TEST_VAR = LiteralVar.create("test")._replace( +TEST_VAR = LiteralVar.create("p")._replace( merge_var_data=VarData( hooks={"useTest": None}, - imports={"test": [ImportVar(tag="test")]}, + imports={"test": [ImportVar(tag="p")]}, state="Test", ) ) @@ -1233,31 +1233,31 @@ def test_stateful_banner(): EVENT_CHAIN_VAR = TEST_VAR.to(EventChain) ARG_VAR = Var(_js_expr="arg") -TEST_VAR_DICT_OF_DICT = LiteralVar.create({"a": {"b": "test"}})._replace( +TEST_VAR_DICT_OF_DICT = LiteralVar.create({"a": {"b": "p"}})._replace( + merge_var_data=TEST_VAR._var_data +) +FORMATTED_TEST_VAR_DICT_OF_DICT = LiteralVar.create({"a": {"b": "foopbar"}})._replace( merge_var_data=TEST_VAR._var_data ) -FORMATTED_TEST_VAR_DICT_OF_DICT = LiteralVar.create( - {"a": {"b": "footestbar"}} -)._replace(merge_var_data=TEST_VAR._var_data) -TEST_VAR_LIST_OF_LIST = LiteralVar.create([["test"]])._replace( +TEST_VAR_LIST_OF_LIST = LiteralVar.create([["p"]])._replace( merge_var_data=TEST_VAR._var_data ) -FORMATTED_TEST_VAR_LIST_OF_LIST = LiteralVar.create([["footestbar"]])._replace( +FORMATTED_TEST_VAR_LIST_OF_LIST = LiteralVar.create([["foopbar"]])._replace( merge_var_data=TEST_VAR._var_data ) -TEST_VAR_LIST_OF_LIST_OF_LIST = LiteralVar.create([[["test"]]])._replace( +TEST_VAR_LIST_OF_LIST_OF_LIST = LiteralVar.create([[["p"]]])._replace( + merge_var_data=TEST_VAR._var_data +) +FORMATTED_TEST_VAR_LIST_OF_LIST_OF_LIST = LiteralVar.create([[["foopbar"]]])._replace( merge_var_data=TEST_VAR._var_data ) -FORMATTED_TEST_VAR_LIST_OF_LIST_OF_LIST = LiteralVar.create( - [[["footestbar"]]] -)._replace(merge_var_data=TEST_VAR._var_data) -TEST_VAR_LIST_OF_DICT = LiteralVar.create([{"a": "test"}])._replace( +TEST_VAR_LIST_OF_DICT = LiteralVar.create([{"a": "p"}])._replace( merge_var_data=TEST_VAR._var_data ) -FORMATTED_TEST_VAR_LIST_OF_DICT = LiteralVar.create([{"a": "footestbar"}])._replace( +FORMATTED_TEST_VAR_LIST_OF_DICT = LiteralVar.create([{"a": "foopbar"}])._replace( merge_var_data=TEST_VAR._var_data )