reflex 0.7.14a5__py3-none-any.whl → 0.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

Files changed (236) hide show
  1. reflex/.templates/jinja/app/rxconfig.py.jinja2 +4 -1
  2. reflex/.templates/jinja/web/package.json.jinja2 +1 -1
  3. reflex/.templates/jinja/web/pages/_app.js.jinja2 +21 -11
  4. reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
  5. reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
  6. reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +4 -0
  7. reflex/.templates/jinja/web/styles/styles.css.jinja2 +1 -0
  8. reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -8
  9. reflex/.templates/web/app/entry.client.js +8 -0
  10. reflex/.templates/web/app/routes.js +10 -0
  11. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -37
  12. reflex/.templates/web/postcss.config.js +1 -1
  13. reflex/.templates/web/react-router.config.js +6 -0
  14. reflex/.templates/web/styles/__reflex_style_reset.css +399 -0
  15. reflex/.templates/web/utils/client_side_routing.js +21 -19
  16. reflex/.templates/web/utils/react-theme.js +92 -0
  17. reflex/.templates/web/utils/state.js +251 -100
  18. reflex/.templates/web/vite-plugin-safari-cachebust.js +160 -0
  19. reflex/.templates/web/vite.config.js +39 -0
  20. reflex/__init__.py +1 -6
  21. reflex/__init__.pyi +327 -192
  22. reflex/app.py +103 -152
  23. reflex/base.py +1 -87
  24. reflex/compiler/compiler.py +70 -19
  25. reflex/compiler/templates.py +3 -3
  26. reflex/compiler/utils.py +91 -33
  27. reflex/components/__init__.py +0 -2
  28. reflex/components/__init__.pyi +34 -18
  29. reflex/components/base/__init__.py +1 -5
  30. reflex/components/base/__init__.pyi +30 -21
  31. reflex/components/base/app_wrap.pyi +7 -7
  32. reflex/components/base/body.pyi +7 -7
  33. reflex/components/base/document.py +18 -14
  34. reflex/components/base/document.pyi +88 -38
  35. reflex/components/base/error_boundary.pyi +7 -7
  36. reflex/components/base/fragment.pyi +7 -7
  37. reflex/components/base/link.pyi +12 -12
  38. reflex/components/base/meta.py +4 -15
  39. reflex/components/base/meta.pyi +31 -31
  40. reflex/components/base/script.py +60 -58
  41. reflex/components/base/script.pyi +248 -34
  42. reflex/components/base/strict_mode.pyi +7 -7
  43. reflex/components/component.py +146 -217
  44. reflex/components/core/__init__.py +1 -0
  45. reflex/components/core/__init__.pyi +77 -37
  46. reflex/components/core/auto_scroll.pyi +7 -7
  47. reflex/components/core/banner.pyi +33 -33
  48. reflex/components/core/client_side_routing.py +7 -6
  49. reflex/components/core/client_side_routing.pyi +8 -59
  50. reflex/components/core/clipboard.pyi +7 -7
  51. reflex/components/core/debounce.py +1 -0
  52. reflex/components/core/debounce.pyi +7 -7
  53. reflex/components/core/foreach.py +5 -4
  54. reflex/components/core/helmet.py +14 -0
  55. reflex/components/{next/base.pyi → core/helmet.pyi} +12 -10
  56. reflex/components/core/html.pyi +7 -7
  57. reflex/components/core/match.py +3 -3
  58. reflex/components/core/sticky.pyi +21 -20
  59. reflex/components/core/upload.py +4 -2
  60. reflex/components/core/upload.pyi +26 -25
  61. reflex/components/datadisplay/__init__.pyi +13 -7
  62. reflex/components/datadisplay/code.py +14 -79
  63. reflex/components/datadisplay/code.pyi +11 -13
  64. reflex/components/datadisplay/dataeditor.pyi +38 -15
  65. reflex/components/datadisplay/shiki_code_block.py +5 -3
  66. reflex/components/datadisplay/shiki_code_block.pyi +16 -15
  67. reflex/components/dynamic.py +5 -5
  68. reflex/components/el/__init__.pyi +506 -246
  69. reflex/components/el/element.pyi +7 -7
  70. reflex/components/el/elements/__init__.pyi +504 -245
  71. reflex/components/el/elements/base.pyi +7 -7
  72. reflex/components/el/elements/forms.pyi +146 -101
  73. reflex/components/el/elements/inline.pyi +142 -142
  74. reflex/components/el/elements/media.pyi +131 -130
  75. reflex/components/el/elements/metadata.pyi +32 -32
  76. reflex/components/el/elements/other.pyi +37 -37
  77. reflex/components/el/elements/scripts.pyi +17 -17
  78. reflex/components/el/elements/sectioning.pyi +77 -77
  79. reflex/components/el/elements/tables.pyi +52 -52
  80. reflex/components/el/elements/typography.pyi +77 -77
  81. reflex/components/field.py +175 -0
  82. reflex/components/gridjs/datatable.py +2 -2
  83. reflex/components/gridjs/datatable.pyi +14 -14
  84. reflex/components/lucide/icon.py +6 -2
  85. reflex/components/lucide/icon.pyi +19 -17
  86. reflex/components/markdown/markdown.py +5 -3
  87. reflex/components/markdown/markdown.pyi +7 -7
  88. reflex/components/moment/moment.py +1 -1
  89. reflex/components/moment/moment.pyi +7 -7
  90. reflex/components/plotly/plotly.py +12 -6
  91. reflex/components/plotly/plotly.pyi +50 -49
  92. reflex/components/props.py +376 -27
  93. reflex/components/radix/__init__.pyi +123 -65
  94. reflex/components/radix/primitives/__init__.pyi +6 -4
  95. reflex/components/radix/primitives/accordion.py +8 -1
  96. reflex/components/radix/primitives/accordion.pyi +37 -37
  97. reflex/components/radix/primitives/base.pyi +12 -12
  98. reflex/components/radix/primitives/drawer.pyi +56 -55
  99. reflex/components/radix/primitives/form.pyi +63 -53
  100. reflex/components/radix/primitives/progress.pyi +26 -25
  101. reflex/components/radix/primitives/slider.pyi +27 -27
  102. reflex/components/radix/themes/__init__.pyi +5 -6
  103. reflex/components/radix/themes/base.py +3 -3
  104. reflex/components/radix/themes/base.pyi +42 -42
  105. reflex/components/radix/themes/color_mode.py +5 -6
  106. reflex/components/radix/themes/color_mode.pyi +17 -17
  107. reflex/components/radix/themes/components/__init__.pyi +75 -38
  108. reflex/components/radix/themes/components/alert_dialog.pyi +37 -37
  109. reflex/components/radix/themes/components/aspect_ratio.pyi +7 -7
  110. reflex/components/radix/themes/components/avatar.pyi +7 -7
  111. reflex/components/radix/themes/components/badge.pyi +7 -7
  112. reflex/components/radix/themes/components/button.pyi +7 -7
  113. reflex/components/radix/themes/components/callout.pyi +26 -25
  114. reflex/components/radix/themes/components/card.pyi +7 -7
  115. reflex/components/radix/themes/components/checkbox.pyi +16 -15
  116. reflex/components/radix/themes/components/checkbox_cards.pyi +12 -12
  117. reflex/components/radix/themes/components/checkbox_group.pyi +12 -12
  118. reflex/components/radix/themes/components/context_menu.pyi +67 -67
  119. reflex/components/radix/themes/components/data_list.pyi +22 -22
  120. reflex/components/radix/themes/components/dialog.pyi +36 -35
  121. reflex/components/radix/themes/components/dropdown_menu.pyi +42 -42
  122. reflex/components/radix/themes/components/hover_card.pyi +21 -20
  123. reflex/components/radix/themes/components/icon_button.pyi +7 -7
  124. reflex/components/radix/themes/components/inset.pyi +7 -7
  125. reflex/components/radix/themes/components/popover.pyi +22 -22
  126. reflex/components/radix/themes/components/progress.pyi +7 -7
  127. reflex/components/radix/themes/components/radio.pyi +7 -7
  128. reflex/components/radix/themes/components/radio_cards.pyi +12 -12
  129. reflex/components/radix/themes/components/radio_group.pyi +21 -20
  130. reflex/components/radix/themes/components/scroll_area.pyi +7 -7
  131. reflex/components/radix/themes/components/segmented_control.pyi +12 -12
  132. reflex/components/radix/themes/components/select.pyi +46 -45
  133. reflex/components/radix/themes/components/separator.pyi +7 -7
  134. reflex/components/radix/themes/components/skeleton.pyi +7 -7
  135. reflex/components/radix/themes/components/slider.pyi +17 -9
  136. reflex/components/radix/themes/components/spinner.pyi +7 -7
  137. reflex/components/radix/themes/components/switch.pyi +7 -7
  138. reflex/components/radix/themes/components/table.pyi +37 -37
  139. reflex/components/radix/themes/components/tabs.pyi +26 -25
  140. reflex/components/radix/themes/components/text_area.pyi +15 -9
  141. reflex/components/radix/themes/components/text_field.pyi +32 -19
  142. reflex/components/radix/themes/components/tooltip.pyi +7 -7
  143. reflex/components/radix/themes/layout/__init__.pyi +27 -14
  144. reflex/components/radix/themes/layout/base.pyi +7 -7
  145. reflex/components/radix/themes/layout/box.pyi +7 -7
  146. reflex/components/radix/themes/layout/center.pyi +7 -7
  147. reflex/components/radix/themes/layout/container.pyi +7 -7
  148. reflex/components/radix/themes/layout/flex.pyi +7 -7
  149. reflex/components/radix/themes/layout/grid.pyi +7 -7
  150. reflex/components/radix/themes/layout/list.pyi +26 -25
  151. reflex/components/radix/themes/layout/section.pyi +7 -7
  152. reflex/components/radix/themes/layout/spacer.pyi +7 -7
  153. reflex/components/radix/themes/layout/stack.pyi +17 -17
  154. reflex/components/radix/themes/typography/__init__.pyi +7 -5
  155. reflex/components/radix/themes/typography/blockquote.pyi +7 -7
  156. reflex/components/radix/themes/typography/code.pyi +7 -7
  157. reflex/components/radix/themes/typography/heading.pyi +7 -7
  158. reflex/components/radix/themes/typography/link.py +46 -11
  159. reflex/components/radix/themes/typography/link.pyi +312 -9
  160. reflex/components/radix/themes/typography/text.pyi +36 -35
  161. reflex/components/react_player/audio.pyi +10 -8
  162. reflex/components/react_player/react_player.pyi +7 -7
  163. reflex/components/react_player/video.pyi +10 -8
  164. reflex/components/recharts/__init__.pyi +208 -100
  165. reflex/components/recharts/cartesian.py +10 -8
  166. reflex/components/recharts/cartesian.pyi +90 -94
  167. reflex/components/recharts/charts.py +4 -2
  168. reflex/components/recharts/charts.pyi +49 -49
  169. reflex/components/recharts/general.pyi +31 -31
  170. reflex/components/recharts/polar.py +8 -4
  171. reflex/components/recharts/polar.pyi +23 -23
  172. reflex/components/recharts/recharts.py +2 -2
  173. reflex/components/recharts/recharts.pyi +12 -12
  174. reflex/components/sonner/toast.py +3 -3
  175. reflex/components/sonner/toast.pyi +9 -9
  176. reflex/config.py +10 -113
  177. reflex/constants/__init__.py +2 -2
  178. reflex/constants/base.py +28 -11
  179. reflex/constants/compiler.py +12 -3
  180. reflex/constants/event.py +1 -0
  181. reflex/constants/installer.py +26 -20
  182. reflex/constants/route.py +27 -8
  183. reflex/constants/state.py +2 -0
  184. reflex/custom_components/custom_components.py +0 -14
  185. reflex/environment.py +77 -5
  186. reflex/event.py +178 -81
  187. reflex/experimental/__init__.py +0 -30
  188. reflex/istate/__init__.py +69 -0
  189. reflex/istate/manager.py +1 -0
  190. reflex/istate/proxy.py +5 -3
  191. reflex/page.py +0 -27
  192. reflex/plugins/__init__.py +3 -2
  193. reflex/plugins/base.py +5 -1
  194. reflex/plugins/shared_tailwind.py +215 -0
  195. reflex/plugins/sitemap.py +206 -0
  196. reflex/plugins/tailwind_v3.py +15 -108
  197. reflex/plugins/tailwind_v4.py +18 -110
  198. reflex/reflex.py +1 -0
  199. reflex/route.py +157 -75
  200. reflex/state.py +171 -155
  201. reflex/testing.py +86 -16
  202. reflex/utils/build.py +38 -82
  203. reflex/utils/exec.py +83 -175
  204. reflex/utils/export.py +2 -2
  205. reflex/utils/format.py +1 -5
  206. reflex/utils/imports.py +5 -16
  207. reflex/utils/misc.py +67 -0
  208. reflex/utils/prerequisites.py +66 -68
  209. reflex/utils/processes.py +24 -47
  210. reflex/utils/pyi_generator.py +44 -49
  211. reflex/utils/serializers.py +14 -1
  212. reflex/utils/telemetry.py +0 -15
  213. reflex/utils/types.py +197 -62
  214. reflex/vars/__init__.py +2 -0
  215. reflex/vars/base.py +367 -134
  216. {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/METADATA +15 -8
  217. reflex-0.8.0.dist-info/RECORD +403 -0
  218. reflex/.templates/web/next.config.js +0 -7
  219. reflex/components/base/head.py +0 -20
  220. reflex/components/base/head.pyi +0 -116
  221. reflex/components/next/__init__.py +0 -10
  222. reflex/components/next/base.py +0 -7
  223. reflex/components/next/image.py +0 -117
  224. reflex/components/next/image.pyi +0 -94
  225. reflex/components/next/link.py +0 -20
  226. reflex/components/next/link.pyi +0 -67
  227. reflex/components/next/video.py +0 -38
  228. reflex/components/next/video.pyi +0 -68
  229. reflex/components/suneditor/__init__.py +0 -5
  230. reflex/components/suneditor/editor.py +0 -269
  231. reflex/components/suneditor/editor.pyi +0 -199
  232. reflex/experimental/layout.py +0 -254
  233. reflex-0.7.14a5.dist-info/RECORD +0 -407
  234. {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/WHEEL +0 -0
  235. {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/entry_points.txt +0 -0
  236. {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -2,17 +2,280 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from pydantic import ValidationError
5
+ from collections.abc import Callable
6
+ from dataclasses import _MISSING_TYPE, MISSING
7
+ from typing import Any, TypeVar, get_args, get_origin
6
8
 
7
- from reflex.base import Base
9
+ from typing_extensions import dataclass_transform
10
+
11
+ from reflex.components.field import BaseField, FieldBasedMeta
8
12
  from reflex.utils import format
9
13
  from reflex.utils.exceptions import InvalidPropValueError
14
+ from reflex.utils.serializers import serializer
15
+ from reflex.utils.types import is_union
10
16
  from reflex.vars.object import LiteralObjectVar
11
17
 
18
+ PROPS_FIELD_TYPE = TypeVar("PROPS_FIELD_TYPE")
19
+
20
+
21
+ def _get_props_subclass(field_type: Any) -> type | None:
22
+ """Extract the Props subclass from a field type annotation.
23
+
24
+ Args:
25
+ field_type: The type annotation to check.
26
+
27
+ Returns:
28
+ The Props subclass if found, None otherwise.
29
+ """
30
+ from reflex.utils.types import typehint_issubclass
31
+
32
+ # For direct class types, we can return them directly if they're Props subclasses
33
+ if isinstance(field_type, type):
34
+ return field_type if typehint_issubclass(field_type, PropsBase) else None
35
+
36
+ # For Union types, check each union member
37
+ if is_union(field_type):
38
+ for arg in get_args(field_type):
39
+ result = _get_props_subclass(arg)
40
+ if result is not None:
41
+ return result
42
+
43
+ return None
44
+
45
+
46
+ def _find_props_in_list_annotation(field_type: Any) -> type | None:
47
+ """Find Props subclass within a list type annotation.
48
+
49
+ Args:
50
+ field_type: The type annotation to check (e.g., list[SomeProps] or list[SomeProps] | None).
51
+
52
+ Returns:
53
+ The Props subclass if found in a list annotation, None otherwise.
54
+ """
55
+ origin = get_origin(field_type)
56
+ if origin is list:
57
+ args = get_args(field_type)
58
+ if args:
59
+ return _get_props_subclass(args[0])
60
+
61
+ # Handle Union types - check if any union member is a list
62
+ if is_union(field_type):
63
+ for arg in get_args(field_type):
64
+ if arg is not type(None): # Skip None from Optional
65
+ list_element = _find_props_in_list_annotation(arg)
66
+ if list_element is not None:
67
+ return list_element
68
+
69
+ return None
70
+
71
+
72
+ class PropsField(BaseField[PROPS_FIELD_TYPE]):
73
+ """A field for a props class."""
74
+
75
+ def __init__(
76
+ self,
77
+ default: PROPS_FIELD_TYPE | _MISSING_TYPE = MISSING,
78
+ default_factory: Callable[[], PROPS_FIELD_TYPE] | None = None,
79
+ annotated_type: type[Any] | _MISSING_TYPE = MISSING,
80
+ ) -> None:
81
+ """Initialize the field.
82
+
83
+ Args:
84
+ default: The default value for the field.
85
+ default_factory: The default factory for the field.
86
+ annotated_type: The annotated type for the field.
87
+ """
88
+ super().__init__(default, default_factory, annotated_type)
89
+ self._name: str = "" # Will be set by metaclass
90
+
91
+ @property
92
+ def required(self) -> bool:
93
+ """Check if the field is required (for Pydantic compatibility).
94
+
95
+ Returns:
96
+ True if the field has no default value or factory.
97
+ """
98
+ return self.default is MISSING and self.default_factory is None
99
+
100
+ @property
101
+ def name(self) -> str | None:
102
+ """Field name (for Pydantic compatibility).
103
+
104
+ Note: This is set by the metaclass when processing fields.
105
+
106
+ Returns:
107
+ The field name if set, None otherwise.
108
+ """
109
+ return getattr(self, "_name", None)
110
+
111
+ def get_default(self) -> Any:
112
+ """Get the default value (for Pydantic compatibility).
113
+
114
+ Returns:
115
+ The default value for the field, or None if required.
116
+ """
117
+ try:
118
+ return self.default_value()
119
+ except ValueError:
120
+ # Field is required (no default)
121
+ return None
122
+
123
+ def __repr__(self) -> str:
124
+ """Represent the field in a readable format.
125
+
126
+ Returns:
127
+ The string representation of the field.
128
+ """
129
+ annotated_type_str = (
130
+ f", annotated_type={self.annotated_type!r}"
131
+ if self.annotated_type is not MISSING
132
+ else ""
133
+ )
134
+ if self.default is not MISSING:
135
+ return f"PropsField(default={self.default!r}{annotated_type_str})"
136
+ return (
137
+ f"PropsField(default_factory={self.default_factory!r}{annotated_type_str})"
138
+ )
139
+
140
+
141
+ def props_field(
142
+ default: PROPS_FIELD_TYPE | _MISSING_TYPE = MISSING,
143
+ default_factory: Callable[[], PROPS_FIELD_TYPE] | None = None,
144
+ ) -> PROPS_FIELD_TYPE:
145
+ """Create a field for a props class.
146
+
147
+ Args:
148
+ default: The default value for the field.
149
+ default_factory: The default factory for the field.
150
+
151
+ Returns:
152
+ The field for the props class.
153
+
154
+ Raises:
155
+ ValueError: If both default and default_factory are specified.
156
+ """
157
+ if default is not MISSING and default_factory is not None:
158
+ msg = "cannot specify both default and default_factory"
159
+ raise ValueError(msg)
160
+ return PropsField( # pyright: ignore [reportReturnType]
161
+ default=default,
162
+ default_factory=default_factory,
163
+ annotated_type=MISSING,
164
+ )
165
+
12
166
 
13
- class PropsBase(Base):
167
+ @dataclass_transform(field_specifiers=(props_field,))
168
+ class PropsBaseMeta(FieldBasedMeta):
169
+ """Meta class for PropsBase."""
170
+
171
+ @classmethod
172
+ def _process_annotated_fields(
173
+ cls,
174
+ namespace: dict[str, Any],
175
+ annotations: dict[str, Any],
176
+ inherited_fields: dict[str, PropsField],
177
+ ) -> dict[str, PropsField]:
178
+ own_fields: dict[str, PropsField] = {}
179
+
180
+ for key, annotation in annotations.items():
181
+ value = namespace.get(key, MISSING)
182
+
183
+ if value is MISSING:
184
+ # Field with only annotation, no default value
185
+ field = PropsField(annotated_type=annotation, default=None)
186
+ elif not isinstance(value, PropsField):
187
+ # Field with default value
188
+ field = PropsField(annotated_type=annotation, default=value)
189
+ else:
190
+ # Field is already a PropsField, update annotation
191
+ field = PropsField(
192
+ annotated_type=annotation,
193
+ default=value.default,
194
+ default_factory=value.default_factory,
195
+ )
196
+
197
+ own_fields[key] = field
198
+
199
+ return own_fields
200
+
201
+ @classmethod
202
+ def _create_field(
203
+ cls,
204
+ annotated_type: Any,
205
+ default: Any = MISSING,
206
+ default_factory: Callable[[], Any] | None = None,
207
+ ) -> PropsField:
208
+ return PropsField(
209
+ annotated_type=annotated_type,
210
+ default=default,
211
+ default_factory=default_factory,
212
+ )
213
+
214
+ @classmethod
215
+ def _finalize_fields(
216
+ cls,
217
+ namespace: dict[str, Any],
218
+ inherited_fields: dict[str, PropsField],
219
+ own_fields: dict[str, PropsField],
220
+ ) -> None:
221
+ # Call parent implementation
222
+ super()._finalize_fields(namespace, inherited_fields, own_fields)
223
+
224
+ # Add Pydantic compatibility
225
+ namespace["__fields__"] = namespace["_fields"]
226
+
227
+
228
+ class PropsBase(metaclass=PropsBaseMeta):
14
229
  """Base for a class containing props that can be serialized as a JS object."""
15
230
 
231
+ def __init__(self, **kwargs):
232
+ """Initialize the props with field values.
233
+
234
+ Args:
235
+ **kwargs: The field values to set.
236
+ """
237
+ # Set field values from kwargs with nested object instantiation
238
+ for key, value in kwargs.items():
239
+ field_info = self.get_fields().get(key)
240
+ if field_info:
241
+ field_type = field_info.annotated_type
242
+
243
+ # Check if this field expects a specific Props type and we got a dict
244
+ if isinstance(value, dict):
245
+ props_class = _get_props_subclass(field_type)
246
+ if props_class is not None:
247
+ value = props_class(**value)
248
+
249
+ # Check if this field expects a list of Props and we got a list of dicts
250
+ elif isinstance(value, list):
251
+ element_type = _find_props_in_list_annotation(field_type)
252
+ if element_type is not None:
253
+ # Convert each dict in the list to the appropriate Props class
254
+ value = [
255
+ element_type(**item) if isinstance(item, dict) else item
256
+ for item in value
257
+ ]
258
+
259
+ setattr(self, key, value)
260
+
261
+ # Set default values for fields not provided
262
+ for field_name, field in self.get_fields().items():
263
+ if field_name not in kwargs:
264
+ if field.default is not MISSING:
265
+ setattr(self, field_name, field.default)
266
+ elif field.default_factory is not None:
267
+ setattr(self, field_name, field.default_factory())
268
+ # Note: Fields with no default and no factory remain unset (required fields)
269
+
270
+ @classmethod
271
+ def get_fields(cls) -> dict[str, Any]:
272
+ """Get the fields of the object.
273
+
274
+ Returns:
275
+ The fields of the object.
276
+ """
277
+ return getattr(cls, "_fields", {})
278
+
16
279
  def json(self) -> str:
17
280
  """Convert the object to a json-like string.
18
281
 
@@ -27,31 +290,117 @@ class PropsBase(Base):
27
290
  {format.to_camel_case(key): value for key, value in self.dict().items()}
28
291
  ).json()
29
292
 
30
- def dict(self, *args, **kwargs):
293
+ def dict(
294
+ self,
295
+ exclude_none: bool = True,
296
+ include: set[str] | None = None,
297
+ exclude: set[str] | None = None,
298
+ **kwargs,
299
+ ):
31
300
  """Convert the object to a dictionary.
32
301
 
33
302
  Keys will be converted to camelCase.
34
303
  By default, None values are excluded (exclude_none=True).
35
304
 
36
305
  Args:
37
- *args: Arguments to pass to the parent class.
38
- **kwargs: Keyword arguments to pass to the parent class.
306
+ exclude_none: Whether to exclude None values.
307
+ include: Fields to include in the output.
308
+ exclude: Fields to exclude from the output.
309
+ **kwargs: Additional keyword arguments (for compatibility).
39
310
 
40
311
  Returns:
41
312
  The object as a dictionary.
42
313
  """
43
- kwargs.setdefault("exclude_none", True)
44
- return {
45
- format.to_camel_case(key): value
46
- for key, value in super().dict(*args, **kwargs).items()
47
- }
314
+ result = {}
48
315
 
316
+ for field_name in self.get_fields():
317
+ if hasattr(self, field_name):
318
+ value = getattr(self, field_name)
49
319
 
50
- class NoExtrasAllowedProps(Base):
320
+ # Apply include/exclude filters
321
+ if include is not None and field_name not in include:
322
+ continue
323
+ if exclude is not None and field_name in exclude:
324
+ continue
325
+
326
+ # Apply exclude_none logic
327
+ if exclude_none and value is None:
328
+ continue
329
+
330
+ # Recursively convert nested structures
331
+ value = self._convert_to_camel_case(
332
+ value, exclude_none, include, exclude
333
+ )
334
+
335
+ # Convert key to camelCase
336
+ camel_key = format.to_camel_case(field_name)
337
+ result[camel_key] = value
338
+
339
+ return result
340
+
341
+ def _convert_to_camel_case(
342
+ self,
343
+ value: Any,
344
+ exclude_none: bool = True,
345
+ include: set[str] | None = None,
346
+ exclude: set[str] | None = None,
347
+ ) -> Any:
348
+ """Recursively convert nested dictionaries and lists to camelCase.
349
+
350
+ Args:
351
+ value: The value to convert.
352
+ exclude_none: Whether to exclude None values.
353
+ include: Fields to include in the output.
354
+ exclude: Fields to exclude from the output.
355
+
356
+ Returns:
357
+ The converted value with camelCase keys.
358
+ """
359
+ if isinstance(value, PropsBase):
360
+ # Convert nested PropsBase objects
361
+ return value.dict(
362
+ exclude_none=exclude_none, include=include, exclude=exclude
363
+ )
364
+ if isinstance(value, dict):
365
+ # Convert dictionary keys to camelCase
366
+ return {
367
+ format.to_camel_case(k): self._convert_to_camel_case(
368
+ v, exclude_none, include, exclude
369
+ )
370
+ for k, v in value.items()
371
+ if not (exclude_none and v is None)
372
+ }
373
+ if isinstance(value, (list, tuple)):
374
+ # Convert list/tuple items recursively
375
+ return [
376
+ self._convert_to_camel_case(item, exclude_none, include, exclude)
377
+ for item in value
378
+ ]
379
+ # Return primitive values as-is
380
+ return value
381
+
382
+
383
+ @serializer(to=dict)
384
+ def serialize_props_base(value: PropsBase) -> dict:
385
+ """Serialize a PropsBase instance.
386
+
387
+ Unlike serialize_base, this preserves callables (lambdas) since they're
388
+ needed for AG Grid and other components that process them on the frontend.
389
+
390
+ Args:
391
+ value: The PropsBase instance to serialize.
392
+
393
+ Returns:
394
+ Dictionary representation of the PropsBase instance.
395
+ """
396
+ return value.dict()
397
+
398
+
399
+ class NoExtrasAllowedProps(PropsBase):
51
400
  """A class that holds props to be passed or applied to a component with no extra props allowed."""
52
401
 
53
402
  def __init__(self, component_name: str | None = None, **kwargs):
54
- """Initialize the props.
403
+ """Initialize the props with validation.
55
404
 
56
405
  Args:
57
406
  component_name: The custom name of the component.
@@ -61,17 +410,17 @@ class NoExtrasAllowedProps(Base):
61
410
  InvalidPropValueError: If invalid props are passed on instantiation.
62
411
  """
63
412
  component_name = component_name or type(self).__name__
64
- try:
65
- super().__init__(**kwargs)
66
- except ValidationError as e:
67
- invalid_fields = ", ".join([error["loc"][0] for error in e.errors()]) # pyright: ignore [reportCallIssue, reportArgumentType]
68
- supported_props_str = ", ".join(f'"{field}"' for field in self.get_fields())
69
- msg = f"Invalid prop(s) {invalid_fields} for {component_name!r}. Supported props are {supported_props_str}"
70
- raise InvalidPropValueError(msg) from None
71
-
72
- class Config: # pyright: ignore [reportIncompatibleVariableOverride]
73
- """Pydantic config."""
74
-
75
- arbitrary_types_allowed = True
76
- use_enum_values = True
77
- extra = "forbid"
413
+
414
+ # Validate fields BEFORE setting them
415
+ known_fields = set(self.__class__.get_fields().keys())
416
+ provided_fields = set(kwargs.keys())
417
+ invalid_fields = provided_fields - known_fields
418
+
419
+ if invalid_fields:
420
+ invalid_fields_str = ", ".join(invalid_fields)
421
+ supported_props_str = ", ".join(f'"{field}"' for field in known_fields)
422
+ msg = f"Invalid prop(s) {invalid_fields_str} for {component_name!r}. Supported props are {supported_props_str}"
423
+ raise InvalidPropValueError(msg)
424
+
425
+ # Use parent class initialization after validation
426
+ super().__init__(**kwargs)
@@ -3,68 +3,126 @@
3
3
  # This file was generated by `reflex/utils/pyi_generator.py`!
4
4
  # ------------------------------------------------------
5
5
 
6
- from . import primitives as primitives
7
- from . import themes as themes
8
- from .primitives.accordion import accordion as accordion
9
- from .primitives.drawer import drawer as drawer
10
- from .primitives.form import form as form
11
- from .themes.base import theme as theme
12
- from .themes.base import theme_panel as theme_panel
13
- from .themes.color_mode import color_mode as color_mode
14
- from .themes.components.alert_dialog import alert_dialog as alert_dialog
15
- from .themes.components.aspect_ratio import aspect_ratio as aspect_ratio
16
- from .themes.components.avatar import avatar as avatar
17
- from .themes.components.badge import badge as badge
18
- from .themes.components.button import button as button
19
- from .themes.components.callout import callout as callout
20
- from .themes.components.card import card as card
21
- from .themes.components.checkbox import checkbox as checkbox
22
- from .themes.components.checkbox_cards import checkbox_cards as checkbox_cards
23
- from .themes.components.checkbox_group import checkbox_group as checkbox_group
24
- from .themes.components.context_menu import context_menu as context_menu
25
- from .themes.components.data_list import data_list as data_list
26
- from .themes.components.dialog import dialog as dialog
27
- from .themes.components.dropdown_menu import dropdown_menu as dropdown_menu
28
- from .themes.components.dropdown_menu import menu as menu
29
- from .themes.components.hover_card import hover_card as hover_card
30
- from .themes.components.icon_button import icon_button as icon_button
31
- from .themes.components.inset import inset as inset
32
- from .themes.components.popover import popover as popover
33
- from .themes.components.progress import progress as progress
34
- from .themes.components.radio_cards import radio_cards as radio_cards
35
- from .themes.components.radio_group import radio as radio
36
- from .themes.components.radio_group import radio_group as radio_group
37
- from .themes.components.scroll_area import scroll_area as scroll_area
38
- from .themes.components.segmented_control import segmented_control as segmented_control
39
- from .themes.components.select import select as select
40
- from .themes.components.separator import divider as divider
41
- from .themes.components.separator import separator as separator
42
- from .themes.components.skeleton import skeleton as skeleton
43
- from .themes.components.slider import slider as slider
44
- from .themes.components.spinner import spinner as spinner
45
- from .themes.components.switch import switch as switch
46
- from .themes.components.table import table as table
47
- from .themes.components.tabs import tabs as tabs
48
- from .themes.components.text_area import text_area as text_area
49
- from .themes.components.text_field import input as input
50
- from .themes.components.text_field import text_field as text_field
51
- from .themes.components.tooltip import tooltip as tooltip
52
- from .themes.layout.box import box as box
53
- from .themes.layout.center import center as center
54
- from .themes.layout.container import container as container
55
- from .themes.layout.flex import flex as flex
56
- from .themes.layout.grid import grid as grid
57
- from .themes.layout.list import list_item as list_item
58
- from .themes.layout.list import list_ns as list # noqa: F401
59
- from .themes.layout.list import ordered_list as ordered_list
60
- from .themes.layout.list import unordered_list as unordered_list
61
- from .themes.layout.section import section as section
62
- from .themes.layout.spacer import spacer as spacer
63
- from .themes.layout.stack import hstack as hstack
64
- from .themes.layout.stack import stack as stack
65
- from .themes.layout.stack import vstack as vstack
66
- from .themes.typography.blockquote import blockquote as blockquote
67
- from .themes.typography.code import code as code
68
- from .themes.typography.heading import heading as heading
69
- from .themes.typography.link import link as link
70
- from .themes.typography.text import text as text
6
+ from . import primitives, themes
7
+ from .primitives.accordion import accordion
8
+ from .primitives.drawer import drawer
9
+ from .primitives.form import form
10
+ from .themes.base import theme, theme_panel
11
+ from .themes.color_mode import color_mode
12
+ from .themes.components.alert_dialog import alert_dialog
13
+ from .themes.components.aspect_ratio import aspect_ratio
14
+ from .themes.components.avatar import avatar
15
+ from .themes.components.badge import badge
16
+ from .themes.components.button import button
17
+ from .themes.components.callout import callout
18
+ from .themes.components.card import card
19
+ from .themes.components.checkbox import checkbox
20
+ from .themes.components.checkbox_cards import checkbox_cards
21
+ from .themes.components.checkbox_group import checkbox_group
22
+ from .themes.components.context_menu import context_menu
23
+ from .themes.components.data_list import data_list
24
+ from .themes.components.dialog import dialog
25
+ from .themes.components.dropdown_menu import dropdown_menu, menu
26
+ from .themes.components.hover_card import hover_card
27
+ from .themes.components.icon_button import icon_button
28
+ from .themes.components.inset import inset
29
+ from .themes.components.popover import popover
30
+ from .themes.components.progress import progress
31
+ from .themes.components.radio_cards import radio_cards
32
+ from .themes.components.radio_group import radio, radio_group
33
+ from .themes.components.scroll_area import scroll_area
34
+ from .themes.components.segmented_control import segmented_control
35
+ from .themes.components.select import select
36
+ from .themes.components.separator import divider, separator
37
+ from .themes.components.skeleton import skeleton
38
+ from .themes.components.slider import slider
39
+ from .themes.components.spinner import spinner
40
+ from .themes.components.switch import switch
41
+ from .themes.components.table import table
42
+ from .themes.components.tabs import tabs
43
+ from .themes.components.text_area import text_area
44
+ from .themes.components.text_field import input, text_field
45
+ from .themes.components.tooltip import tooltip
46
+ from .themes.layout.box import box
47
+ from .themes.layout.center import center
48
+ from .themes.layout.container import container
49
+ from .themes.layout.flex import flex
50
+ from .themes.layout.grid import grid
51
+ from .themes.layout.list import list_item, ordered_list, unordered_list
52
+ from .themes.layout.list import list_ns as list
53
+ from .themes.layout.section import section
54
+ from .themes.layout.spacer import spacer
55
+ from .themes.layout.stack import hstack, stack, vstack
56
+ from .themes.typography.blockquote import blockquote
57
+ from .themes.typography.code import code
58
+ from .themes.typography.heading import heading
59
+ from .themes.typography.link import link
60
+ from .themes.typography.text import text
61
+
62
+ __all__ = [
63
+ "accordion",
64
+ "alert_dialog",
65
+ "aspect_ratio",
66
+ "avatar",
67
+ "badge",
68
+ "blockquote",
69
+ "box",
70
+ "button",
71
+ "callout",
72
+ "card",
73
+ "center",
74
+ "checkbox",
75
+ "checkbox_cards",
76
+ "checkbox_group",
77
+ "code",
78
+ "color_mode",
79
+ "container",
80
+ "context_menu",
81
+ "data_list",
82
+ "dialog",
83
+ "divider",
84
+ "drawer",
85
+ "dropdown_menu",
86
+ "flex",
87
+ "form",
88
+ "grid",
89
+ "heading",
90
+ "hover_card",
91
+ "hstack",
92
+ "icon_button",
93
+ "input",
94
+ "inset",
95
+ "link",
96
+ "list",
97
+ "list_item",
98
+ "menu",
99
+ "ordered_list",
100
+ "popover",
101
+ "primitives",
102
+ "progress",
103
+ "radio",
104
+ "radio_cards",
105
+ "radio_group",
106
+ "scroll_area",
107
+ "section",
108
+ "segmented_control",
109
+ "select",
110
+ "separator",
111
+ "skeleton",
112
+ "slider",
113
+ "spacer",
114
+ "spinner",
115
+ "stack",
116
+ "switch",
117
+ "table",
118
+ "tabs",
119
+ "text",
120
+ "text_area",
121
+ "text_field",
122
+ "theme",
123
+ "theme_panel",
124
+ "themes",
125
+ "tooltip",
126
+ "unordered_list",
127
+ "vstack",
128
+ ]
@@ -3,7 +3,9 @@
3
3
  # This file was generated by `reflex/utils/pyi_generator.py`!
4
4
  # ------------------------------------------------------
5
5
 
6
- from .accordion import accordion as accordion
7
- from .drawer import drawer as drawer
8
- from .form import form as form
9
- from .progress import progress as progress
6
+ from .accordion import accordion
7
+ from .drawer import drawer
8
+ from .form import form
9
+ from .progress import progress
10
+
11
+ __all__ = ["accordion", "drawer", "form", "progress"]
@@ -334,7 +334,11 @@ class AccordionHeader(AccordionComponent):
334
334
  Returns:
335
335
  The style of the component.
336
336
  """
337
- return {"display": "flex"}
337
+ return {
338
+ "display": "flex",
339
+ # Reset some values to ensure consistent styling without tailwind reset.
340
+ "margin": "0",
341
+ }
338
342
 
339
343
 
340
344
  class AccordionTrigger(AccordionComponent):
@@ -399,6 +403,9 @@ class AccordionTrigger(AccordionComponent):
399
403
  "color": "var(--accent-contrast)",
400
404
  },
401
405
  },
406
+ # Reset some values to ensure consistent styling without tailwind reset.
407
+ "background": "none",
408
+ "border": "none",
402
409
  }
403
410
 
404
411