1414from functools import wraps
1515from hashlib import md5
1616from types import SimpleNamespace
17- from typing import (
18- TYPE_CHECKING ,
19- Annotated ,
20- Any ,
21- ClassVar ,
22- Generic ,
23- TypeVar ,
24- cast ,
25- get_args ,
26- get_origin ,
27- )
17+ from typing import TYPE_CHECKING , Any , ClassVar , TypeVar , cast , get_args , get_origin
2818
2919from rich .markup import escape
3020from typing_extensions import dataclass_transform
3323from reflex .compiler .templates import STATEFUL_COMPONENT
3424from reflex .components .core .breakpoints import Breakpoints
3525from reflex .components .dynamic import load_dynamic_serializer
26+ from reflex .components .field import BaseField , FieldBasedMeta
3627from reflex .components .tags import Tag
3728from reflex .constants import (
3829 Dirs ,
7566FIELD_TYPE = TypeVar ("FIELD_TYPE" )
7667
7768
78- class ComponentField (Generic [FIELD_TYPE ]):
69+ class ComponentField (BaseField [FIELD_TYPE ]):
7970 """A field for a component."""
8071
8172 def __init__ (
@@ -93,30 +84,8 @@ def __init__(
9384 is_javascript: Whether the field is a javascript property.
9485 annotated_type: The annotated type for the field.
9586 """
96- self .default = default
97- self .default_factory = default_factory
87+ super ().__init__ (default , default_factory , annotated_type )
9888 self .is_javascript = is_javascript
99- self .outer_type_ = self .annotated_type = annotated_type
100- type_origin = get_origin (annotated_type ) or annotated_type
101- if type_origin is Annotated :
102- type_origin = annotated_type .__origin__ # pyright: ignore [reportAttributeAccessIssue]
103- self .type_ = self .type_origin = type_origin
104-
105- def default_value (self ) -> FIELD_TYPE :
106- """Get the default value for the field.
107-
108- Returns:
109- The default value for the field.
110-
111- Raises:
112- ValueError: If no default value or factory is provided.
113- """
114- if self .default is not MISSING :
115- return self .default
116- if self .default_factory is not None :
117- return self .default_factory ()
118- msg = "No default value or factory provided."
119- raise ValueError (msg )
12089
12190 def __repr__ (self ) -> str :
12291 """Represent the field in a readable format.
@@ -163,7 +132,7 @@ def field(
163132
164133
165134@dataclass_transform (kw_only_default = True , field_specifiers = (field ,))
166- class BaseComponentMeta (ABCMeta ):
135+ class BaseComponentMeta (FieldBasedMeta , ABCMeta ):
167136 """Meta class for BaseComponent."""
168137
169138 if TYPE_CHECKING :
@@ -172,46 +141,24 @@ class BaseComponentMeta(ABCMeta):
172141 _fields : Mapping [str , ComponentField ]
173142 _js_fields : Mapping [str , ComponentField ]
174143
175- def __new__ (cls , name : str , bases : tuple [type ], namespace : dict [str , Any ]) -> type :
176- """Create a new class.
177-
178- Args:
179- name: The name of the class.
180- bases: The bases of the class.
181- namespace: The namespace of the class.
182-
183- Returns:
184- The new class.
185- """
186- # Add the field to the class
187- inherited_fields : dict [str , ComponentField ] = {}
188- own_fields : dict [str , ComponentField ] = {}
189- resolved_annotations = types .resolve_annotations (
144+ @classmethod
145+ def _resolve_annotations (
146+ cls , namespace : dict [str , Any ], name : str
147+ ) -> dict [str , Any ]:
148+ return types .resolve_annotations (
190149 namespace .get ("__annotations__" , {}), namespace ["__module__" ]
191150 )
192151
193- for base in bases [::- 1 ]:
194- if hasattr (base , "_inherited_fields" ):
195- inherited_fields .update (base ._inherited_fields )
196- for base in bases [::- 1 ]:
197- if hasattr (base , "_own_fields" ):
198- inherited_fields .update (base ._own_fields )
199-
200- for key , value , inherited_field in [
201- (key , value , inherited_field )
202- for key , value in namespace .items ()
203- if key not in resolved_annotations
204- and ((inherited_field := inherited_fields .get (key )) is not None )
205- ]:
206- new_value = ComponentField (
207- default = value ,
208- is_javascript = inherited_field .is_javascript ,
209- annotated_type = inherited_field .annotated_type ,
210- )
211-
212- own_fields [key ] = new_value
152+ @classmethod
153+ def _process_annotated_fields (
154+ cls ,
155+ namespace : dict [str , Any ],
156+ annotations : dict [str , Any ],
157+ inherited_fields : dict [str , ComponentField ],
158+ ) -> dict [str , ComponentField ]:
159+ own_fields : dict [str , ComponentField ] = {}
213160
214- for key , annotation in resolved_annotations .items ():
161+ for key , annotation in annotations .items ():
215162 value = namespace .get (key , MISSING )
216163
217164 if types .is_classvar (annotation ):
@@ -244,16 +191,63 @@ def __new__(cls, name: str, bases: tuple[type], namespace: dict[str, Any]) -> ty
244191
245192 own_fields [key ] = value
246193
247- namespace ["_own_fields" ] = own_fields
248- namespace ["_inherited_fields" ] = inherited_fields
249- all_fields = inherited_fields | own_fields
250- namespace ["_fields" ] = all_fields
194+ return own_fields
195+
196+ @classmethod
197+ def _create_field (
198+ cls ,
199+ annotated_type : Any ,
200+ default : Any = MISSING ,
201+ default_factory : Callable [[], Any ] | None = None ,
202+ ) -> ComponentField :
203+ return ComponentField (
204+ annotated_type = annotated_type ,
205+ default = default ,
206+ default_factory = default_factory ,
207+ is_javascript = True , # Default for components
208+ )
209+
210+ @classmethod
211+ def _process_field_overrides (
212+ cls ,
213+ namespace : dict [str , Any ],
214+ annotations : dict [str , Any ],
215+ inherited_fields : dict [str , Any ],
216+ ) -> dict [str , ComponentField ]:
217+ own_fields : dict [str , ComponentField ] = {}
218+
219+ for key , value , inherited_field in [
220+ (key , value , inherited_field )
221+ for key , value in namespace .items ()
222+ if key not in annotations
223+ and ((inherited_field := inherited_fields .get (key )) is not None )
224+ ]:
225+ new_field = ComponentField (
226+ default = value ,
227+ is_javascript = inherited_field .is_javascript ,
228+ annotated_type = inherited_field .annotated_type ,
229+ )
230+ own_fields [key ] = new_field
231+
232+ return own_fields
233+
234+ @classmethod
235+ def _finalize_fields (
236+ cls ,
237+ namespace : dict [str , Any ],
238+ inherited_fields : dict [str , ComponentField ],
239+ own_fields : dict [str , ComponentField ],
240+ ) -> None :
241+ # Call parent implementation
242+ super ()._finalize_fields (namespace , inherited_fields , own_fields )
243+
244+ # Add JavaScript fields mapping
245+ all_fields = namespace ["_fields" ]
251246 namespace ["_js_fields" ] = {
252247 key : value
253248 for key , value in all_fields .items ()
254249 if value .is_javascript is True
255250 }
256- return super ().__new__ (cls , name , bases , namespace )
257251
258252
259253class BaseComponent (metaclass = BaseComponentMeta ):
0 commit comments