reflex 0.4.9a2__py3-none-any.whl → 0.5.0a1__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 (131) hide show
  1. reflex/.templates/apps/blank/code/blank.py +19 -16
  2. reflex/.templates/apps/demo/code/pages/datatable.py +4 -4
  3. reflex/.templates/apps/demo/code/pages/forms.py +2 -2
  4. reflex/.templates/web/utils/helpers/debounce.js +17 -0
  5. reflex/.templates/web/utils/helpers/throttle.js +22 -0
  6. reflex/.templates/web/utils/state.js +21 -3
  7. reflex/__init__.py +6 -1
  8. reflex/__init__.pyi +4 -1
  9. reflex/app.py +157 -140
  10. reflex/app_module_for_backend.py +1 -1
  11. reflex/base.py +13 -15
  12. reflex/compiler/compiler.py +10 -1
  13. reflex/compiler/utils.py +3 -30
  14. reflex/components/__init__.py +1 -0
  15. reflex/components/chakra/datadisplay/list.py +1 -3
  16. reflex/components/chakra/datadisplay/list.pyi +3 -3
  17. reflex/components/chakra/disclosure/accordion.py +1 -1
  18. reflex/components/chakra/forms/pininput.pyi +1 -1
  19. reflex/components/chakra/media/icon.py +2 -2
  20. reflex/components/component.py +279 -32
  21. reflex/components/core/__init__.py +2 -2
  22. reflex/components/core/cond.py +1 -10
  23. reflex/components/core/debounce.py +5 -2
  24. reflex/components/core/debounce.pyi +4 -2
  25. reflex/components/core/foreach.py +1 -16
  26. reflex/components/core/html.py +6 -0
  27. reflex/components/core/match.py +2 -17
  28. reflex/components/core/upload.py +42 -1
  29. reflex/components/core/upload.pyi +199 -1
  30. reflex/components/datadisplay/code.py +7 -3
  31. reflex/components/datadisplay/code.pyi +3 -1
  32. reflex/components/el/elements/forms.py +1 -1
  33. reflex/components/el/elements/forms.pyi +1 -1
  34. reflex/components/lucide/icon.py +5 -13
  35. reflex/components/lucide/icon.pyi +0 -1
  36. reflex/components/markdown/markdown.py +5 -23
  37. reflex/components/markdown/markdown.pyi +1 -4
  38. reflex/components/radix/primitives/accordion.py +227 -406
  39. reflex/components/radix/primitives/accordion.pyi +369 -28
  40. reflex/components/radix/primitives/form.py +33 -29
  41. reflex/components/radix/primitives/form.pyi +7 -2
  42. reflex/components/radix/primitives/progress.py +17 -9
  43. reflex/components/radix/primitives/progress.pyi +2 -0
  44. reflex/components/radix/primitives/slider.py +30 -18
  45. reflex/components/radix/primitives/slider.pyi +4 -0
  46. reflex/components/radix/themes/base.py +8 -1
  47. reflex/components/radix/themes/base.pyi +79 -1
  48. reflex/components/radix/themes/color_mode.py +74 -30
  49. reflex/components/radix/themes/color_mode.pyi +26 -185
  50. reflex/components/radix/themes/components/__init__.py +17 -0
  51. reflex/components/radix/themes/components/badge.py +2 -1
  52. reflex/components/radix/themes/components/badge.pyi +3 -1
  53. reflex/components/radix/themes/components/button.py +3 -1
  54. reflex/components/radix/themes/components/button.pyi +4 -1
  55. reflex/components/radix/themes/components/checkbox_cards.py +48 -0
  56. reflex/components/radix/themes/components/checkbox_cards.pyi +264 -0
  57. reflex/components/radix/themes/components/checkbox_group.py +42 -0
  58. reflex/components/radix/themes/components/checkbox_group.pyi +253 -0
  59. reflex/components/radix/themes/components/data_list.py +63 -0
  60. reflex/components/radix/themes/components/data_list.pyi +426 -0
  61. reflex/components/radix/themes/components/icon_button.py +20 -17
  62. reflex/components/radix/themes/components/icon_button.pyi +5 -1
  63. reflex/components/radix/themes/components/progress.py +55 -0
  64. reflex/components/radix/themes/components/progress.pyi +180 -0
  65. reflex/components/radix/themes/components/radio.py +31 -0
  66. reflex/components/radix/themes/components/radio.pyi +169 -0
  67. reflex/components/radix/themes/components/radio_cards.py +48 -0
  68. reflex/components/radix/themes/components/radio_cards.pyi +264 -0
  69. reflex/components/radix/themes/components/radio_group.py +2 -4
  70. reflex/components/radix/themes/components/segmented_control.py +48 -0
  71. reflex/components/radix/themes/components/segmented_control.pyi +262 -0
  72. reflex/components/radix/themes/components/skeleton.py +32 -0
  73. reflex/components/radix/themes/components/skeleton.pyi +106 -0
  74. reflex/components/radix/themes/components/spinner.py +26 -0
  75. reflex/components/radix/themes/components/spinner.pyi +101 -0
  76. reflex/components/radix/themes/components/tabs.py +26 -1
  77. reflex/components/radix/themes/components/tabs.pyi +69 -9
  78. reflex/components/radix/themes/components/text_field.py +101 -71
  79. reflex/components/radix/themes/components/text_field.pyi +81 -499
  80. reflex/components/radix/themes/layout/base.py +2 -2
  81. reflex/components/radix/themes/layout/base.pyi +4 -4
  82. reflex/components/radix/themes/layout/center.py +8 -3
  83. reflex/components/radix/themes/layout/center.pyi +2 -1
  84. reflex/components/radix/themes/layout/container.py +30 -2
  85. reflex/components/radix/themes/layout/container.pyi +9 -30
  86. reflex/components/radix/themes/layout/list.py +10 -5
  87. reflex/components/radix/themes/layout/list.pyi +5 -21
  88. reflex/components/radix/themes/layout/spacer.py +8 -3
  89. reflex/components/radix/themes/layout/spacer.pyi +2 -1
  90. reflex/components/radix/themes/layout/stack.py +7 -1
  91. reflex/components/radix/themes/layout/stack.pyi +3 -3
  92. reflex/components/radix/themes/typography/link.py +10 -2
  93. reflex/components/radix/themes/typography/link.pyi +5 -4
  94. reflex/components/sonner/__init__.py +3 -0
  95. reflex/components/sonner/toast.py +267 -0
  96. reflex/components/sonner/toast.pyi +205 -0
  97. reflex/components/tags/iter_tag.py +9 -6
  98. reflex/config.py +30 -54
  99. reflex/constants/__init__.py +0 -2
  100. reflex/constants/base.py +0 -5
  101. reflex/constants/colors.py +2 -0
  102. reflex/constants/installer.py +5 -1
  103. reflex/constants/route.py +4 -0
  104. reflex/custom_components/custom_components.py +22 -1
  105. reflex/event.py +75 -30
  106. reflex/experimental/__init__.py +5 -0
  107. reflex/experimental/layout.py +24 -6
  108. reflex/model.py +2 -1
  109. reflex/page.py +7 -4
  110. reflex/reflex.py +8 -3
  111. reflex/route.py +39 -0
  112. reflex/state.py +128 -131
  113. reflex/style.py +20 -1
  114. reflex/testing.py +10 -6
  115. reflex/utils/console.py +3 -1
  116. reflex/utils/exec.py +20 -7
  117. reflex/utils/format.py +1 -1
  118. reflex/utils/imports.py +3 -1
  119. reflex/utils/prerequisites.py +141 -20
  120. reflex/utils/processes.py +21 -1
  121. reflex/utils/pyi_generator.py +95 -5
  122. reflex/utils/serializers.py +1 -1
  123. reflex/utils/telemetry.py +26 -4
  124. reflex/utils/types.py +62 -18
  125. reflex/vars.py +11 -5
  126. {reflex-0.4.9a2.dist-info → reflex-0.5.0a1.dist-info}/METADATA +16 -4
  127. {reflex-0.4.9a2.dist-info → reflex-0.5.0a1.dist-info}/RECORD +130 -108
  128. {reflex-0.4.9a2.dist-info → reflex-0.5.0a1.dist-info}/WHEEL +1 -1
  129. reflex/app.pyi +0 -149
  130. {reflex-0.4.9a2.dist-info → reflex-0.5.0a1.dist-info}/LICENSE +0 -0
  131. {reflex-0.4.9a2.dist-info → reflex-0.5.0a1.dist-info}/entry_points.txt +0 -0
reflex/compiler/utils.py CHANGED
@@ -3,19 +3,13 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import os
6
- from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Type, Union
6
+ from typing import Any, Callable, Dict, Optional, Type, Union
7
7
  from urllib.parse import urlparse
8
8
 
9
9
  try:
10
- # TODO The type checking guard can be removed once
11
- # reflex-hosting-cli tools are compatible with pydantic v2
12
-
13
- if not TYPE_CHECKING:
14
- from pydantic.v1.fields import ModelField
15
- else:
16
- raise ModuleNotFoundError
10
+ from pydantic.v1.fields import ModelField
17
11
  except ModuleNotFoundError:
18
- from pydantic.fields import ModelField
12
+ from pydantic.fields import ModelField # type: ignore
19
13
 
20
14
  from reflex import constants
21
15
  from reflex.components.base import (
@@ -392,27 +386,6 @@ def get_stateful_components_path() -> str:
392
386
  )
393
387
 
394
388
 
395
- def get_asset_path(filename: str | None = None) -> str:
396
- """Get the path for an asset.
397
-
398
- Args:
399
- filename: If given, is added to the root path of assets dir.
400
-
401
- Returns:
402
- The path of the asset.
403
- """
404
- console.deprecate(
405
- feature_name="rx.get_asset_path",
406
- reason="use rx.get_upload_dir() instead.",
407
- deprecation_version="0.4.0",
408
- removal_version="0.5.0",
409
- )
410
- if filename is None:
411
- return constants.Dirs.WEB_ASSETS
412
- else:
413
- return os.path.join(constants.Dirs.WEB_ASSETS, filename)
414
-
415
-
416
389
  def add_meta(
417
390
  page: Component,
418
391
  title: str,
@@ -15,6 +15,7 @@ from .next import NextLink, next_link
15
15
  from .plotly import *
16
16
  from .radix import *
17
17
  from .react_player import *
18
+ from .sonner import *
18
19
  from .suneditor import *
19
20
 
20
21
  icon = lucide.icon
@@ -23,9 +23,7 @@ class List(ChakraComponent):
23
23
  style_type: Var[str]
24
24
 
25
25
  @classmethod
26
- def create(
27
- cls, *children, items: list | Var[list] | None = None, **props
28
- ) -> Component:
26
+ def create(cls, *children, items: Var[list] | None = None, **props) -> Component:
29
27
  """Create a list component.
30
28
 
31
29
  Args:
@@ -18,7 +18,7 @@ class List(ChakraComponent):
18
18
  def create( # type: ignore
19
19
  cls,
20
20
  *children,
21
- items: Optional[list | Var[list] | None] = None,
21
+ items: Optional[Union[Var[list], list]] = None,
22
22
  spacing: Optional[Union[Var[str], str]] = None,
23
23
  style_position: Optional[Union[Var[str], str]] = None,
24
24
  style_type: Optional[Union[Var[str], str]] = None,
@@ -178,7 +178,7 @@ class OrderedList(List):
178
178
  def create( # type: ignore
179
179
  cls,
180
180
  *children,
181
- items: Optional[list | Var[list] | None] = None,
181
+ items: Optional[Union[Var[list], list]] = None,
182
182
  spacing: Optional[Union[Var[str], str]] = None,
183
183
  style_position: Optional[Union[Var[str], str]] = None,
184
184
  style_type: Optional[Union[Var[str], str]] = None,
@@ -262,7 +262,7 @@ class UnorderedList(List):
262
262
  def create( # type: ignore
263
263
  cls,
264
264
  *children,
265
- items: Optional[list | Var[list] | None] = None,
265
+ items: Optional[Union[Var[list], list]] = None,
266
266
  spacing: Optional[Union[Var[str], str]] = None,
267
267
  style_position: Optional[Union[Var[str], str]] = None,
268
268
  style_type: Optional[Union[Var[str], str]] = None,
@@ -35,7 +35,7 @@ class Accordion(ChakraComponent):
35
35
  icon_pos="right",
36
36
  allow_multiple: Optional[Var[bool]] = None,
37
37
  allow_toggle: Optional[Var[bool]] = None,
38
- **props
38
+ **props,
39
39
  ) -> Component:
40
40
  """Create an accordion component.
41
41
 
@@ -147,7 +147,7 @@ class PinInputField(ChakraComponent):
147
147
  def create( # type: ignore
148
148
  cls,
149
149
  *children,
150
- index: Optional[Var[int]] = None,
150
+ index: Optional[Union[Var[int], int]] = None,
151
151
  name: Optional[Union[Var[str], str]] = None,
152
152
  style: Optional[Style] = None,
153
153
  key: Optional[Any] = None,
@@ -37,9 +37,9 @@ class Icon(ChakraIconComponent):
37
37
  raise AttributeError(
38
38
  f"Passing children to Icon component is not allowed: remove positional arguments {children} to fix"
39
39
  )
40
- if "tag" not in props.keys():
40
+ if "tag" not in props:
41
41
  raise AttributeError("Missing 'tag' keyword-argument for Icon")
42
- if type(props["tag"]) != str or props["tag"].lower() not in ICON_LIST:
42
+ if not isinstance(props["tag"], str) or props["tag"].lower() not in ICON_LIST:
43
43
  raise ValueError(
44
44
  f"Invalid icon tag: {props['tag']}. Please use one of the following: {sorted(ICON_LIST)}"
45
45
  )
@@ -213,6 +213,91 @@ class Component(BaseComponent, ABC):
213
213
  # State class associated with this component instance
214
214
  State: Optional[Type[reflex.state.State]] = None
215
215
 
216
+ def add_imports(self) -> dict[str, str | ImportVar | list[str | ImportVar]]:
217
+ """Add imports for the component.
218
+
219
+ This method should be implemented by subclasses to add new imports for the component.
220
+
221
+ Implementations do NOT need to call super(). The result of calling
222
+ add_imports in each parent class will be merged internally.
223
+
224
+ Returns:
225
+ The additional imports for this component subclass.
226
+
227
+ The format of the return value is a dictionary where the keys are the
228
+ library names (with optional npm-style version specifications) mapping
229
+ to a single name to be imported, or a list names to be imported.
230
+
231
+ For advanced use cases, the values can be ImportVar instances (for
232
+ example, to provide an alias or mark that an import is the default
233
+ export from the given library).
234
+
235
+ ```python
236
+ return {
237
+ "react": "useEffect",
238
+ "react-draggable": ["DraggableCore", rx.ImportVar(tag="Draggable", is_default=True)],
239
+ }
240
+ ```
241
+ """
242
+ return {}
243
+
244
+ def add_hooks(self) -> list[str]:
245
+ """Add hooks inside the component function.
246
+
247
+ Hooks are pieces of literal Javascript code that is inserted inside the
248
+ React component function.
249
+
250
+ Each logical hook should be a separate string in the list.
251
+
252
+ Common strings will be deduplicated and inserted into the component
253
+ function only once, so define const variables and other identical code
254
+ in their own strings to avoid defining the same const or hook multiple
255
+ times.
256
+
257
+ If a hook depends on specific data from the component instance, be sure
258
+ to use unique values inside the string to _avoid_ deduplication.
259
+
260
+ Implementations do NOT need to call super(). The result of calling
261
+ add_hooks in each parent class will be merged and deduplicated internally.
262
+
263
+ Returns:
264
+ The additional hooks for this component subclass.
265
+
266
+ ```python
267
+ return [
268
+ "const [count, setCount] = useState(0);",
269
+ "useEffect(() => { setCount((prev) => prev + 1); console.log(`mounted ${count} times`); }, []);",
270
+ ]
271
+ ```
272
+ """
273
+ return []
274
+
275
+ def add_custom_code(self) -> list[str]:
276
+ """Add custom Javascript code into the page that contains this component.
277
+
278
+ Custom code is inserted at module level, after any imports.
279
+
280
+ Each string of custom code is deduplicated per-page, so take care to
281
+ avoid defining the same const or function differently from different
282
+ component instances.
283
+
284
+ Custom code is useful for defining global functions or constants which
285
+ can then be referenced inside hooks or used by component vars.
286
+
287
+ Implementations do NOT need to call super(). The result of calling
288
+ add_custom_code in each parent class will be merged and deduplicated internally.
289
+
290
+ Returns:
291
+ The additional custom code for this component subclass.
292
+
293
+ ```python
294
+ return [
295
+ "const translatePoints = (event) => { return { x: event.clientX, y: event.clientY }; };",
296
+ ]
297
+ ```
298
+ """
299
+ return []
300
+
216
301
  @classmethod
217
302
  def __init_subclass__(cls, **kwargs):
218
303
  """Set default properties.
@@ -255,6 +340,7 @@ class Component(BaseComponent, ABC):
255
340
 
256
341
  Raises:
257
342
  TypeError: If an invalid prop is passed.
343
+ ValueError: If an event trigger passed is not valid.
258
344
  """
259
345
  # Set the id and children initially.
260
346
  children = kwargs.get("children", [])
@@ -284,6 +370,12 @@ class Component(BaseComponent, ABC):
284
370
 
285
371
  # Iterate through the kwargs and set the props.
286
372
  for key, value in kwargs.items():
373
+ if key.startswith("on_") and key not in triggers and key not in props:
374
+ raise ValueError(
375
+ f"The {(comp_name := type(self).__name__)} does not take in an `{key}` event trigger. If {comp_name}"
376
+ f" is a third party component make sure to add `{key}` to the component's event triggers. "
377
+ f"visit https://reflex.dev/docs/wrapping-react/logic/#event-triggers for more info."
378
+ )
287
379
  if key in triggers:
288
380
  # Event triggers are bound to event chains.
289
381
  field_type = EventChain
@@ -296,6 +388,8 @@ class Component(BaseComponent, ABC):
296
388
 
297
389
  # Check whether the key is a component prop.
298
390
  if types._issubclass(field_type, Var):
391
+ # Used to store the passed types if var type is a union.
392
+ passed_types = None
299
393
  try:
300
394
  # Try to create a var from the value.
301
395
  kwargs[key] = Var.create(value)
@@ -320,10 +414,25 @@ class Component(BaseComponent, ABC):
320
414
  # If it is not a valid var, check the base types.
321
415
  passed_type = type(value)
322
416
  expected_type = fields[key].outer_type_
323
- if not types._issubclass(passed_type, expected_type):
417
+ if types.is_union(passed_type):
418
+ # We need to check all possible types in the union.
419
+ passed_types = (
420
+ arg for arg in passed_type.__args__ if arg is not type(None)
421
+ )
422
+ if (
423
+ # If the passed var is a union, check if all possible types are valid.
424
+ passed_types
425
+ and not all(
426
+ types._issubclass(pt, expected_type) for pt in passed_types
427
+ )
428
+ ) or (
429
+ # Else just check if the passed var type is valid.
430
+ not passed_types
431
+ and not types._issubclass(passed_type, expected_type)
432
+ ):
324
433
  value_name = value._var_name if isinstance(value, Var) else value
325
434
  raise TypeError(
326
- f"Invalid var passed for prop {key}, expected type {expected_type}, got value {value_name} of type {passed_type}."
435
+ f"Invalid var passed for prop {type(self).__name__}.{key}, expected type {expected_type}, got value {value_name} of type {passed_types or passed_type}."
327
436
  )
328
437
 
329
438
  # Check if the key is an event trigger.
@@ -397,7 +506,7 @@ class Component(BaseComponent, ABC):
397
506
  if isinstance(value, List):
398
507
  events: list[EventSpec] = []
399
508
  for v in value:
400
- if isinstance(v, EventHandler):
509
+ if isinstance(v, (EventHandler, EventSpec)):
401
510
  # Call the event handler to get the event.
402
511
  try:
403
512
  event = call_event_handler(v, args_spec)
@@ -408,9 +517,6 @@ class Component(BaseComponent, ABC):
408
517
 
409
518
  # Add the event to the chain.
410
519
  events.append(event)
411
- elif isinstance(v, EventSpec):
412
- # Add the event to the chain.
413
- events.append(v)
414
520
  elif isinstance(v, Callable):
415
521
  # Call the lambda to get the event chain.
416
522
  events.extend(call_event_fn(v, args_spec))
@@ -502,6 +608,8 @@ class Component(BaseComponent, ABC):
502
608
  def _apply_theme(self, theme: Optional[Component]):
503
609
  """Apply the theme to this component.
504
610
 
611
+ Deprecated. Use add_style instead.
612
+
505
613
  Args:
506
614
  theme: The theme to apply.
507
615
  """
@@ -635,7 +743,7 @@ class Component(BaseComponent, ABC):
635
743
  f"Underscore suffix for prop `{under_prop}`",
636
744
  reason=f"for consistency. Use `{prop}` instead.",
637
745
  deprecation_version="0.4.0",
638
- removal_version="0.5.0",
746
+ removal_version="0.6.0",
639
747
  dedupe=False,
640
748
  )
641
749
  props[prop] = props.pop(under_prop)
@@ -673,44 +781,119 @@ class Component(BaseComponent, ABC):
673
781
 
674
782
  return cls(children=children, **props)
675
783
 
676
- def _add_style(self, style: dict):
677
- """Add additional style to the component.
784
+ def add_style(self) -> Style | None:
785
+ """Add style to the component.
678
786
 
679
- Args:
680
- style: A style dict to apply.
787
+ Downstream components can override this method to return a style dict
788
+ that will be applied to the component.
789
+
790
+ Returns:
791
+ The style to add.
681
792
  """
682
- self.style.update(style)
793
+ return None
794
+
795
+ def _add_style(self) -> Style:
796
+ """Call add_style for all bases in the MRO.
797
+
798
+ Downstream components should NOT override. Use add_style instead.
683
799
 
684
- def _add_style_recursive(self, style: ComponentStyle) -> Component:
800
+ Returns:
801
+ The style to add.
802
+ """
803
+ styles = []
804
+ vars = []
805
+
806
+ # Walk the MRO to call all `add_style` methods.
807
+ for base in self._iter_parent_classes_with_method("add_style"):
808
+ s = base.add_style(self) # type: ignore
809
+ if s is not None:
810
+ styles.append(s)
811
+ vars.append(s._var_data)
812
+
813
+ _style = Style()
814
+ for s in reversed(styles):
815
+ _style.update(s)
816
+
817
+ _style._var_data = VarData.merge(*vars)
818
+ return _style
819
+
820
+ def _get_component_style(self, styles: ComponentStyle) -> Style | None:
821
+ """Get the style to the component from `App.style`.
822
+
823
+ Args:
824
+ styles: The style to apply.
825
+
826
+ Returns:
827
+ The style of the component.
828
+ """
829
+ component_style = None
830
+ if type(self) in styles:
831
+ component_style = Style(styles[type(self)])
832
+ if self.create in styles:
833
+ component_style = Style(styles[self.create])
834
+ return component_style
835
+
836
+ def _add_style_recursive(
837
+ self, style: ComponentStyle, theme: Optional[Component] = None
838
+ ) -> Component:
685
839
  """Add additional style to the component and its children.
686
840
 
841
+ Apply order is as follows (with the latest overriding the earliest):
842
+ 1. Default style from `_add_style`/`add_style`.
843
+ 2. User-defined style from `App.style`.
844
+ 3. User-defined style from `Component.style`.
845
+ 4. style dict and css props passed to the component instance.
846
+
687
847
  Args:
688
848
  style: A dict from component to styling.
849
+ theme: The theme to apply. (for retro-compatibility with deprecated _apply_theme API)
850
+
851
+ Raises:
852
+ UserWarning: If `_add_style` has been overridden.
689
853
 
690
854
  Returns:
691
855
  The component with the additional style.
692
856
  """
693
- component_style = None
694
- if type(self) in style:
695
- # Extract the style for this component.
696
- component_style = Style(style[type(self)])
697
- if self.create in style:
698
- component_style = Style(style[self.create])
699
- if component_style is not None:
700
- # Only add style props that are not overridden.
701
- component_style = {
702
- k: v for k, v in component_style.items() if k not in self.style
703
- }
857
+ # 1. Default style from `_add_style`/`add_style`.
858
+ if type(self)._add_style != Component._add_style:
859
+ raise UserWarning(
860
+ "Do not override _add_style directly. Use add_style instead."
861
+ )
862
+ new_style = self._add_style()
863
+ style_vars = [new_style._var_data]
864
+
865
+ # 2. User-defined style from `App.style`.
866
+ component_style = self._get_component_style(style)
867
+ if component_style:
868
+ new_style.update(component_style)
869
+ style_vars.append(component_style._var_data)
870
+
871
+ # 3. User-defined style from `Component.style`.
872
+ # Apply theme for retro-compatibility with deprecated _apply_theme API
873
+ if type(self)._apply_theme != Component._apply_theme:
874
+ console.deprecate(
875
+ f"{self.__class__.__name__}._apply_theme",
876
+ reason="use add_style instead",
877
+ deprecation_version="0.5.0",
878
+ removal_version="0.6.0",
879
+ )
880
+ self._apply_theme(theme)
881
+
882
+ # 4. style dict and css props passed to the component instance.
883
+ new_style.update(self.style)
884
+ style_vars.append(self.style._var_data)
885
+
886
+ new_style._var_data = VarData.merge(*style_vars)
704
887
 
705
- # Add the style to the component.
706
- self._add_style(component_style)
888
+ # Assign the new style
889
+ self.style = new_style
707
890
 
708
891
  # Recursively add style to the children.
709
892
  for child in self.children:
710
893
  # Skip BaseComponent and StatefulComponent children.
711
894
  if not isinstance(child, Component):
712
895
  continue
713
- child._add_style_recursive(style)
896
+ child._add_style_recursive(style, theme)
714
897
  return self
715
898
 
716
899
  def _get_style(self) -> dict:
@@ -925,6 +1108,30 @@ class Component(BaseComponent, ABC):
925
1108
  return True
926
1109
  return False
927
1110
 
1111
+ @classmethod
1112
+ def _iter_parent_classes_with_method(cls, method: str) -> Iterator[Type[Component]]:
1113
+ """Iterate through parent classes that define a given method.
1114
+
1115
+ Used for handling the `add_*` API functions that internally simulate a super() call chain.
1116
+
1117
+ Args:
1118
+ method: The method to look for.
1119
+
1120
+ Yields:
1121
+ The parent classes that define the method (differently than the base).
1122
+ """
1123
+ seen_methods = set([getattr(Component, method)])
1124
+ for clz in cls.mro():
1125
+ if clz is Component:
1126
+ break
1127
+ if not issubclass(clz, Component):
1128
+ continue
1129
+ method_func = getattr(clz, method, None)
1130
+ if not callable(method_func) or method_func in seen_methods:
1131
+ continue
1132
+ seen_methods.add(method_func)
1133
+ yield clz
1134
+
928
1135
  def _get_custom_code(self) -> str | None:
929
1136
  """Get custom code for the component.
930
1137
 
@@ -947,6 +1154,11 @@ class Component(BaseComponent, ABC):
947
1154
  if custom_code is not None:
948
1155
  code.add(custom_code)
949
1156
 
1157
+ # Add the custom code from add_custom_code method.
1158
+ for clz in self._iter_parent_classes_with_method("add_custom_code"):
1159
+ for item in clz.add_custom_code(self):
1160
+ code.add(item)
1161
+
950
1162
  # Add the custom code for the children.
951
1163
  for child in self.children:
952
1164
  code |= child._get_all_custom_code()
@@ -1082,6 +1294,26 @@ class Component(BaseComponent, ABC):
1082
1294
  var._var_data.imports for var in self._get_vars() if var._var_data
1083
1295
  ]
1084
1296
 
1297
+ # If any subclass implements add_imports, merge the imports.
1298
+ def _make_list(
1299
+ value: str | ImportVar | list[str | ImportVar],
1300
+ ) -> list[str | ImportVar]:
1301
+ if isinstance(value, (str, ImportVar)):
1302
+ return [value]
1303
+ return value
1304
+
1305
+ _added_import_dicts = []
1306
+ for clz in self._iter_parent_classes_with_method("add_imports"):
1307
+ _added_import_dicts.append(
1308
+ {
1309
+ package: [
1310
+ ImportVar(tag=tag) if not isinstance(tag, ImportVar) else tag
1311
+ for tag in _make_list(maybe_tags)
1312
+ ]
1313
+ for package, maybe_tags in clz.add_imports(self).items()
1314
+ }
1315
+ )
1316
+
1085
1317
  return imports.merge_imports(
1086
1318
  *self._get_props_imports(),
1087
1319
  self._get_dependencies_imports(),
@@ -1089,6 +1321,7 @@ class Component(BaseComponent, ABC):
1089
1321
  _imports,
1090
1322
  event_imports,
1091
1323
  *var_imports,
1324
+ *_added_import_dicts,
1092
1325
  )
1093
1326
 
1094
1327
  def _get_all_imports(self, collapse: bool = False) -> imports.ImportDict:
@@ -1224,6 +1457,12 @@ class Component(BaseComponent, ABC):
1224
1457
  if hooks is not None:
1225
1458
  code[hooks] = None
1226
1459
 
1460
+ # Add the hook code from add_hooks for each parent class (this is reversed to preserve
1461
+ # the order of the hooks in the final output)
1462
+ for clz in reversed(tuple(self._iter_parent_classes_with_method("add_hooks"))):
1463
+ for hook in clz.add_hooks(self):
1464
+ code[hook] = None
1465
+
1227
1466
  # Add the hook code for the children.
1228
1467
  for child in self.children:
1229
1468
  code = {**code, **child._get_all_hooks()}
@@ -1397,7 +1636,7 @@ class CustomComponent(Component):
1397
1636
  else:
1398
1637
  value = base_value
1399
1638
  else:
1400
- value = Var.create(value, _var_is_string=type(value) is str)
1639
+ value = Var.create(value, _var_is_string=isinstance(value, str))
1401
1640
 
1402
1641
  # Set the prop.
1403
1642
  self.props[format.to_camel_case(key)] = value
@@ -1516,7 +1755,7 @@ class CustomComponent(Component):
1516
1755
 
1517
1756
 
1518
1757
  def custom_component(
1519
- component_fn: Callable[..., Component]
1758
+ component_fn: Callable[..., Component],
1520
1759
  ) -> Callable[..., CustomComponent]:
1521
1760
  """Create a custom component from a function.
1522
1761
 
@@ -1583,9 +1822,7 @@ class NoSSRComponent(Component):
1583
1822
  library_import = f"const {self.alias if self.alias else self.tag} = dynamic(() => import('{import_name}')"
1584
1823
  mod_import = (
1585
1824
  # https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#with-named-exports
1586
- f".then((mod) => mod.{self.tag})"
1587
- if not self.is_default
1588
- else ""
1825
+ f".then((mod) => mod.{self.tag})" if not self.is_default else ""
1589
1826
  )
1590
1827
  return "".join((library_import, mod_import, opts_fragment))
1591
1828
 
@@ -1940,6 +2177,16 @@ class StatefulComponent(BaseComponent):
1940
2177
  """
1941
2178
  return dict(Tag(name=self.tag))
1942
2179
 
2180
+ def __str__(self) -> str:
2181
+ """Represent the component in React.
2182
+
2183
+ Returns:
2184
+ The code to render the component.
2185
+ """
2186
+ from reflex.compiler.compiler import _compile_component
2187
+
2188
+ return _compile_component(self)
2189
+
1943
2190
  @classmethod
1944
2191
  def compile_from(cls, component: BaseComponent) -> BaseComponent:
1945
2192
  """Walk through the component tree and memoize all stateful components.
@@ -16,7 +16,7 @@ from .responsive import (
16
16
  tablet_only,
17
17
  )
18
18
  from .upload import (
19
- Upload,
19
+ UploadNamespace,
20
20
  cancel_upload,
21
21
  clear_selected_files,
22
22
  get_upload_dir,
@@ -31,4 +31,4 @@ debounce_input = DebounceInput.create
31
31
  foreach = Foreach.create
32
32
  html = Html.create
33
33
  match = Match.create
34
- upload = Upload.create
34
+ upload = UploadNamespace()
@@ -102,15 +102,6 @@ class Cond(MemoizationLeaf):
102
102
  _IS_TRUE_IMPORT,
103
103
  )
104
104
 
105
- def _apply_theme(self, theme: Component):
106
- """Apply the theme to this component.
107
-
108
- Args:
109
- theme: The theme to apply.
110
- """
111
- self.comp1.apply_theme(theme) # type: ignore
112
- self.comp2.apply_theme(theme) # type: ignore
113
-
114
105
 
115
106
  @overload
116
107
  def cond(condition: Any, c1: Component, c2: Any) -> Component:
@@ -172,7 +163,7 @@ def cond(condition: Any, c1: Any, c2: Any = None):
172
163
  def create_var(cond_part):
173
164
  return Var.create_safe(
174
165
  cond_part,
175
- _var_is_string=type(cond_part) is str or isinstance(cond_part, Color),
166
+ _var_is_string=isinstance(cond_part, (str, Color)),
176
167
  )
177
168
 
178
169
  # convert the truth and false cond parts into vars so the _var_data can be obtained.
@@ -1,7 +1,8 @@
1
1
  """Wrapper around react-debounce-input."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
- from typing import Any, Type
5
+ from typing import Any, Type, Union
5
6
 
6
7
  from reflex.components.component import Component
7
8
  from reflex.constants import EventTriggers
@@ -34,7 +35,7 @@ class DebounceInput(Component):
34
35
  force_notify_on_blur: Var[bool]
35
36
 
36
37
  # If provided, create a fully-controlled input
37
- value: Var[str]
38
+ value: Var[Union[str, int, float]]
38
39
 
39
40
  # The ref to attach to the created input
40
41
  input_ref: Var[str]
@@ -120,6 +121,8 @@ class DebounceInput(Component):
120
121
  component = super().create(**props)
121
122
  component._get_style = child._get_style
122
123
  component.event_triggers.update(child.event_triggers)
124
+ component.children = child.children
125
+ component._rename_props = child._rename_props
123
126
  return component
124
127
 
125
128
  def get_event_triggers(self) -> dict[str, Any]:
@@ -7,7 +7,7 @@ from typing import Any, Dict, Literal, Optional, Union, overload
7
7
  from reflex.vars import Var, BaseVar, ComputedVar
8
8
  from reflex.event import EventChain, EventHandler, EventSpec
9
9
  from reflex.style import Style
10
- from typing import Any, Type
10
+ from typing import Any, Type, Union
11
11
  from reflex.components.component import Component
12
12
  from reflex.constants import EventTriggers
13
13
  from reflex.vars import Var, VarData
@@ -24,7 +24,9 @@ class DebounceInput(Component):
24
24
  debounce_timeout: Optional[Union[Var[int], int]] = None,
25
25
  force_notify_by_enter: Optional[Union[Var[bool], bool]] = None,
26
26
  force_notify_on_blur: Optional[Union[Var[bool], bool]] = None,
27
- value: Optional[Union[Var[str], str]] = None,
27
+ value: Optional[
28
+ Union[Var[Union[str, int, float]], Union[str, int, float]]
29
+ ] = None,
28
30
  input_ref: Optional[Union[Var[str], str]] = None,
29
31
  element: Optional[Union[Var[Type[Component]], Type[Component]]] = None,
30
32
  style: Optional[Style] = None,