reflex 0.6.8a2__py3-none-any.whl → 0.7.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 (154) hide show
  1. reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +1 -1
  2. reflex/.templates/jinja/web/pages/_app.js.jinja2 +7 -7
  3. reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
  4. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +1 -4
  5. reflex/.templates/web/utils/state.js +65 -36
  6. reflex/__init__.py +4 -17
  7. reflex/__init__.pyi +1 -2
  8. reflex/app.py +244 -109
  9. reflex/app_mixins/lifespan.py +9 -9
  10. reflex/app_mixins/middleware.py +6 -6
  11. reflex/app_module_for_backend.py +3 -7
  12. reflex/base.py +7 -7
  13. reflex/compiler/compiler.py +8 -0
  14. reflex/compiler/utils.py +35 -6
  15. reflex/components/base/bare.py +1 -1
  16. reflex/components/base/error_boundary.py +2 -1
  17. reflex/components/base/error_boundary.pyi +2 -1
  18. reflex/components/base/meta.py +2 -2
  19. reflex/components/base/strict_mode.py +10 -0
  20. reflex/components/base/strict_mode.pyi +57 -0
  21. reflex/components/component.py +38 -77
  22. reflex/components/core/banner.py +83 -4
  23. reflex/components/core/banner.pyi +86 -0
  24. reflex/components/core/breakpoints.py +3 -1
  25. reflex/components/core/client_side_routing.py +1 -1
  26. reflex/components/core/client_side_routing.pyi +1 -1
  27. reflex/components/core/cond.py +9 -10
  28. reflex/components/core/debounce.py +1 -1
  29. reflex/components/core/foreach.py +23 -3
  30. reflex/components/core/html.py +1 -1
  31. reflex/components/core/match.py +5 -5
  32. reflex/components/core/sticky.py +160 -0
  33. reflex/components/core/sticky.pyi +449 -0
  34. reflex/components/core/upload.py +2 -2
  35. reflex/components/datadisplay/code.py +5 -14
  36. reflex/components/datadisplay/dataeditor.py +7 -4
  37. reflex/components/datadisplay/logo.py +13 -8
  38. reflex/components/datadisplay/shiki_code_block.py +14 -9
  39. reflex/components/dynamic.py +22 -3
  40. reflex/components/el/constants/reflex.py +1 -1
  41. reflex/components/el/element.py +1 -1
  42. reflex/components/el/elements/forms.py +4 -4
  43. reflex/components/el/elements/forms.pyi +4 -4
  44. reflex/components/lucide/icon.py +46 -8
  45. reflex/components/lucide/icon.pyi +54 -0
  46. reflex/components/markdown/markdown.py +10 -8
  47. reflex/components/moment/moment.py +2 -2
  48. reflex/components/next/image.py +16 -4
  49. reflex/components/next/image.pyi +4 -2
  50. reflex/components/next/link.py +1 -1
  51. reflex/components/plotly/plotly.py +5 -5
  52. reflex/components/props.py +3 -3
  53. reflex/components/radix/__init__.pyi +1 -1
  54. reflex/components/radix/primitives/accordion.py +9 -5
  55. reflex/components/radix/primitives/accordion.pyi +3 -1
  56. reflex/components/radix/primitives/drawer.py +5 -2
  57. reflex/components/radix/primitives/drawer.pyi +4 -4
  58. reflex/components/radix/primitives/form.pyi +6 -6
  59. reflex/components/radix/primitives/progress.py +1 -1
  60. reflex/components/radix/primitives/slider.py +1 -1
  61. reflex/components/radix/themes/color_mode.py +11 -9
  62. reflex/components/radix/themes/components/alert_dialog.py +3 -0
  63. reflex/components/radix/themes/components/card.py +1 -1
  64. reflex/components/radix/themes/components/card.pyi +1 -1
  65. reflex/components/radix/themes/components/context_menu.py +5 -0
  66. reflex/components/radix/themes/components/dialog.py +3 -0
  67. reflex/components/radix/themes/components/dropdown_menu.py +5 -0
  68. reflex/components/radix/themes/components/hover_card.py +3 -0
  69. reflex/components/radix/themes/components/icon_button.py +2 -2
  70. reflex/components/radix/themes/components/icon_button.pyi +1 -0
  71. reflex/components/radix/themes/components/popover.py +3 -0
  72. reflex/components/radix/themes/components/radio_cards.py +2 -0
  73. reflex/components/radix/themes/components/radio_group.py +1 -1
  74. reflex/components/radix/themes/components/select.py +3 -0
  75. reflex/components/radix/themes/components/tabs.py +3 -0
  76. reflex/components/radix/themes/components/text_area.py +12 -0
  77. reflex/components/radix/themes/components/text_area.pyi +2 -0
  78. reflex/components/radix/themes/components/text_field.py +1 -1
  79. reflex/components/radix/themes/components/tooltip.py +3 -1
  80. reflex/components/radix/themes/components/tooltip.pyi +1 -0
  81. reflex/components/radix/themes/layout/__init__.pyi +1 -1
  82. reflex/components/radix/themes/layout/list.py +2 -2
  83. reflex/components/radix/themes/layout/stack.py +2 -2
  84. reflex/components/radix/themes/typography/link.py +1 -1
  85. reflex/components/radix/themes/typography/text.py +2 -2
  86. reflex/components/react_player/react_player.py +1 -1
  87. reflex/components/recharts/__init__.py +2 -0
  88. reflex/components/recharts/__init__.pyi +2 -0
  89. reflex/components/recharts/charts.py +15 -15
  90. reflex/components/recharts/general.py +19 -4
  91. reflex/components/recharts/general.pyi +55 -4
  92. reflex/components/recharts/polar.py +2 -2
  93. reflex/components/recharts/recharts.py +4 -4
  94. reflex/components/sonner/toast.py +15 -13
  95. reflex/components/sonner/toast.pyi +6 -6
  96. reflex/components/suneditor/editor.py +6 -4
  97. reflex/components/suneditor/editor.pyi +2 -2
  98. reflex/components/tags/iter_tag.py +3 -3
  99. reflex/components/tags/tag.py +25 -3
  100. reflex/config.py +48 -15
  101. reflex/constants/__init__.py +1 -0
  102. reflex/constants/base.py +4 -1
  103. reflex/constants/compiler.py +5 -2
  104. reflex/constants/config.py +8 -1
  105. reflex/constants/installer.py +9 -9
  106. reflex/constants/style.py +1 -1
  107. reflex/custom_components/custom_components.py +9 -7
  108. reflex/event.py +130 -161
  109. reflex/experimental/__init__.py +19 -11
  110. reflex/experimental/client_state.py +53 -28
  111. reflex/experimental/hooks.py +5 -5
  112. reflex/experimental/layout.py +8 -5
  113. reflex/experimental/layout.pyi +1 -1
  114. reflex/experimental/misc.py +3 -3
  115. reflex/istate/wrappers.py +1 -1
  116. reflex/middleware/hydrate_middleware.py +2 -2
  117. reflex/model.py +11 -6
  118. reflex/page.py +3 -3
  119. reflex/reflex.py +90 -19
  120. reflex/route.py +1 -1
  121. reflex/state.py +358 -401
  122. reflex/style.py +27 -3
  123. reflex/testing.py +29 -23
  124. reflex/utils/build.py +6 -2
  125. reflex/utils/codespaces.py +1 -4
  126. reflex/utils/compat.py +6 -5
  127. reflex/utils/console.py +52 -16
  128. reflex/utils/exceptions.py +76 -26
  129. reflex/utils/exec.py +69 -74
  130. reflex/utils/export.py +6 -1
  131. reflex/utils/format.py +7 -39
  132. reflex/utils/imports.py +2 -2
  133. reflex/utils/lazy_loader.py +7 -1
  134. reflex/utils/path_ops.py +28 -14
  135. reflex/utils/prerequisites.py +324 -65
  136. reflex/utils/processes.py +45 -32
  137. reflex/utils/pyi_generator.py +30 -25
  138. reflex/utils/registry.py +4 -4
  139. reflex/utils/serializers.py +1 -1
  140. reflex/utils/telemetry.py +5 -4
  141. reflex/utils/types.py +42 -18
  142. reflex/vars/base.py +650 -333
  143. reflex/vars/datetime.py +6 -7
  144. reflex/vars/dep_tracking.py +344 -0
  145. reflex/vars/function.py +11 -5
  146. reflex/vars/number.py +31 -43
  147. reflex/vars/object.py +63 -62
  148. reflex/vars/sequence.py +79 -67
  149. {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/METADATA +7 -8
  150. {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/RECORD +153 -149
  151. {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/WHEEL +1 -1
  152. reflex/experimental/assets.py +0 -37
  153. {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/LICENSE +0 -0
  154. {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/entry_points.txt +0 -0
@@ -14,7 +14,7 @@ from reflex.components.radix.themes.layout.box import Box
14
14
  from reflex.constants.colors import Color
15
15
  from reflex.event import set_clipboard
16
16
  from reflex.style import Style
17
- from reflex.utils import console, format
17
+ from reflex.utils import format
18
18
  from reflex.utils.imports import ImportVar
19
19
  from reflex.vars.base import LiteralVar, Var, VarData
20
20
 
@@ -382,7 +382,7 @@ for theme_name in dir(Theme):
382
382
  class CodeBlock(Component, MarkdownComponentMap):
383
383
  """A code block."""
384
384
 
385
- library = "react-syntax-highlighter@15.6.0"
385
+ library = "react-syntax-highlighter@15.6.1"
386
386
 
387
387
  tag = "PrismAsyncLight"
388
388
 
@@ -438,6 +438,8 @@ class CodeBlock(Component, MarkdownComponentMap):
438
438
  can_copy = props.pop("can_copy", False)
439
439
  copy_button = props.pop("copy_button", None)
440
440
 
441
+ # react-syntax-highlighter doesn't have an explicit "light" or "dark" theme so we use one-light and one-dark
442
+ # themes respectively to ensure code compatibility.
441
443
  if "theme" not in props:
442
444
  # Default color scheme responds to global color mode.
443
445
  props["theme"] = color_mode_cond(
@@ -445,20 +447,9 @@ class CodeBlock(Component, MarkdownComponentMap):
445
447
  dark=Theme.one_dark,
446
448
  )
447
449
 
448
- # react-syntax-highlighter doesn't have an explicit "light" or "dark" theme so we use one-light and one-dark
449
- # themes respectively to ensure code compatibility.
450
- if "theme" in props and not isinstance(props["theme"], Var):
451
- props["theme"] = getattr(Theme, format.to_snake_case(props["theme"])) # type: ignore
452
- console.deprecate(
453
- feature_name="theme prop as string",
454
- reason="Use code_block.themes instead.",
455
- deprecation_version="0.6.0",
456
- removal_version="0.7.0",
457
- )
458
-
459
450
  if can_copy:
460
451
  code = children[0]
461
- copy_button = ( # type: ignore
452
+ copy_button = (
462
453
  copy_button
463
454
  if copy_button is not None
464
455
  else Button.create(
@@ -165,7 +165,7 @@ class DataEditor(NoSSRComponent):
165
165
 
166
166
  tag = "DataEditor"
167
167
  is_default = True
168
- library: str = "@glideapps/glide-data-grid@^6.0.3"
168
+ library: str | None = "@glideapps/glide-data-grid@^6.0.3"
169
169
  lib_dependencies: List[str] = [
170
170
  "lodash@^4.17.21",
171
171
  "react-responsive-carousel@^3.2.7",
@@ -321,6 +321,8 @@ class DataEditor(NoSSRComponent):
321
321
  Returns:
322
322
  The import dict.
323
323
  """
324
+ if self.library is None:
325
+ return {}
324
326
  return {
325
327
  "": f"{format.format_library_name(self.library)}/dist/index.css",
326
328
  self.library: "GridCellKind",
@@ -343,9 +345,9 @@ class DataEditor(NoSSRComponent):
343
345
  data_callback = self.get_cell_content._js_expr
344
346
  else:
345
347
  data_callback = f"getData_{editor_id}"
346
- self.get_cell_content = Var(_js_expr=data_callback) # type: ignore
348
+ self.get_cell_content = Var(_js_expr=data_callback)
347
349
 
348
- code = [f"function {data_callback}([col, row])" "{"]
350
+ code = [f"function {data_callback}([col, row]){{"]
349
351
 
350
352
  columns_path = str(self.columns)
351
353
  data_path = str(self.data)
@@ -385,7 +387,8 @@ class DataEditor(NoSSRComponent):
385
387
  raise ValueError(
386
388
  "DataEditor data must be an ArrayVar if rows is not provided."
387
389
  )
388
- props["rows"] = data.length() if isinstance(data, Var) else len(data)
390
+
391
+ props["rows"] = data.length() if isinstance(data, ArrayVar) else len(data)
389
392
 
390
393
  if not isinstance(columns, Var) and len(columns):
391
394
  if types.is_dataframe(type(data)) or (
@@ -5,20 +5,22 @@ from typing import Union
5
5
  import reflex as rx
6
6
 
7
7
 
8
- def svg_logo(color: Union[str, rx.Var[str]] = rx.color_mode_cond("#110F1F", "white")):
8
+ def svg_logo(
9
+ color: Union[str, rx.Var[str]] = rx.color_mode_cond("#110F1F", "white"),
10
+ **props,
11
+ ):
9
12
  """A Reflex logo SVG.
10
13
 
11
14
  Args:
12
15
  color: The color of the logo.
16
+ props: Extra props to pass to the svg component.
13
17
 
14
18
  Returns:
15
19
  The Reflex logo SVG.
16
20
  """
17
21
 
18
- def logo_path(d):
19
- return rx.el.svg.path(
20
- d=d,
21
- )
22
+ def logo_path(d: str):
23
+ return rx.el.svg.path(d=d)
22
24
 
23
25
  paths = [
24
26
  "M0 11.5999V0.399902H8.96V4.8799H6.72V2.6399H2.24V4.8799H6.72V7.1199H2.24V11.5999H0ZM6.72 11.5999V7.1199H8.96V11.5999H6.72Z",
@@ -31,11 +33,14 @@ def svg_logo(color: Union[str, rx.Var[str]] = rx.color_mode_cond("#110F1F", "whi
31
33
 
32
34
  return rx.el.svg(
33
35
  *[logo_path(d) for d in paths],
34
- width="56",
35
- height="12",
36
- viewBox="0 0 56 12",
36
+ rx.el.title("Reflex"),
37
+ aria_label="Reflex",
38
+ role="img",
39
+ width=props.pop("width", "56"),
40
+ height=props.pop("height", "12"),
37
41
  fill=color,
38
42
  xmlns="http://www.w3.org/2000/svg",
43
+ **props,
39
44
  )
40
45
 
41
46
 
@@ -602,7 +602,7 @@ class ShikiCodeBlock(Component, MarkdownComponentMap):
602
602
 
603
603
  transformer_styles = {}
604
604
  # Collect styles from transformers and wrapper
605
- for transformer in code_block.transformers._var_value: # type: ignore
605
+ for transformer in code_block.transformers._var_value: # pyright: ignore [reportAttributeAccessIssue]
606
606
  if isinstance(transformer, ShikiBaseTransformers) and transformer.style:
607
607
  transformer_styles.update(transformer.style)
608
608
  transformer_styles.update(code_wrapper_props.pop("style", {}))
@@ -621,18 +621,22 @@ class ShikiCodeBlock(Component, MarkdownComponentMap):
621
621
 
622
622
  Returns:
623
623
  Imports for the component.
624
+
625
+ Raises:
626
+ ValueError: If the transformers are not of type LiteralVar.
624
627
  """
625
628
  imports = defaultdict(list)
629
+ if not isinstance(self.transformers, LiteralVar):
630
+ raise ValueError(
631
+ f"transformers should be a LiteralVar type. Got {type(self.transformers)} instead."
632
+ )
626
633
  for transformer in self.transformers._var_value:
627
634
  if isinstance(transformer, ShikiBaseTransformers):
628
635
  imports[transformer.library].extend(
629
636
  [ImportVar(tag=str(fn)) for fn in transformer.fns]
630
637
  )
631
- (
638
+ if transformer.library not in self.lib_dependencies:
632
639
  self.lib_dependencies.append(transformer.library)
633
- if transformer.library not in self.lib_dependencies
634
- else None
635
- )
636
640
  return imports
637
641
 
638
642
  @classmethod
@@ -653,8 +657,9 @@ class ShikiCodeBlock(Component, MarkdownComponentMap):
653
657
  raise ValueError(
654
658
  f"the function names should be str names of functions in the specified transformer: {library!r}"
655
659
  )
656
- return ShikiBaseTransformers( # type: ignore
657
- library=library, fns=[FunctionStringVar.create(fn) for fn in fns]
660
+ return ShikiBaseTransformers(
661
+ library=library,
662
+ fns=[FunctionStringVar.create(fn) for fn in fns], # pyright: ignore [reportCallIssue]
658
663
  )
659
664
 
660
665
  def _render(self, props: dict[str, Any] | None = None):
@@ -757,13 +762,13 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
757
762
 
758
763
  if can_copy:
759
764
  code = children[0]
760
- copy_button = ( # type: ignore
765
+ copy_button = (
761
766
  copy_button
762
767
  if copy_button is not None
763
768
  else Button.create(
764
769
  Icon.create(tag="copy", size=16, color=color("gray", 11)),
765
770
  on_click=[
766
- set_clipboard(cls._strip_transformer_triggers(code)), # type: ignore
771
+ set_clipboard(cls._strip_transformer_triggers(code)),
767
772
  copy_script(),
768
773
  ],
769
774
  style=Style(
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Union
4
4
 
5
5
  from reflex import constants
6
6
  from reflex.utils import imports
7
- from reflex.utils.exceptions import DynamicComponentMissingLibrary
7
+ from reflex.utils.exceptions import DynamicComponentMissingLibraryError
8
8
  from reflex.utils.format import format_library_name
9
9
  from reflex.utils.serializers import serializer
10
10
  from reflex.vars import Var, get_unique_variable_name
@@ -36,13 +36,15 @@ def bundle_library(component: Union["Component", str]):
36
36
  component: The component to bundle the library with.
37
37
 
38
38
  Raises:
39
- DynamicComponentMissingLibrary: Raised when a dynamic component is missing a library.
39
+ DynamicComponentMissingLibraryError: Raised when a dynamic component is missing a library.
40
40
  """
41
41
  if isinstance(component, str):
42
42
  bundled_libraries.add(component)
43
43
  return
44
44
  if component.library is None:
45
- raise DynamicComponentMissingLibrary("Component must have a library to bundle.")
45
+ raise DynamicComponentMissingLibraryError(
46
+ "Component must have a library to bundle."
47
+ )
46
48
  bundled_libraries.add(format_library_name(component.library))
47
49
 
48
50
 
@@ -136,6 +138,23 @@ def load_dynamic_serializer():
136
138
 
137
139
  module_code_lines.insert(0, "const React = window.__reflex.react;")
138
140
 
141
+ function_line = next(
142
+ index
143
+ for index, line in enumerate(module_code_lines)
144
+ if line.startswith("export default function")
145
+ )
146
+
147
+ module_code_lines = [
148
+ line
149
+ for _, line in sorted(
150
+ enumerate(module_code_lines),
151
+ key=lambda x: (
152
+ not (x[1].startswith("import ") and x[0] < function_line),
153
+ x[0],
154
+ ),
155
+ )
156
+ ]
157
+
139
158
  return "\n".join(
140
159
  [
141
160
  "//__reflex_evaluate",
@@ -48,4 +48,4 @@ PROP_TO_ELEMENTS = {
48
48
  ELEMENT_TO_PROPS = defaultdict(list)
49
49
  for prop, elements in PROP_TO_ELEMENTS.items():
50
50
  for el in elements:
51
- ELEMENT_TO_PROPS[el].append(prop) # type: ignore
51
+ ELEMENT_TO_PROPS[el].append(prop)
@@ -6,7 +6,7 @@ from reflex.components.component import Component
6
6
  class Element(Component):
7
7
  """The base class for all raw HTML elements."""
8
8
 
9
- def __eq__(self, other):
9
+ def __eq__(self, other: object):
10
10
  """Two elements are equal if they have the same tag.
11
11
 
12
12
  Args:
@@ -102,7 +102,7 @@ class Fieldset(Element):
102
102
  name: Var[Union[str, int, bool]]
103
103
 
104
104
 
105
- def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]:
105
+ def on_submit_event_spec() -> Tuple[Var[dict[str, Any]]]:
106
106
  """Event handler spec for the on_submit event.
107
107
 
108
108
  Returns:
@@ -111,7 +111,7 @@ def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]:
111
111
  return (FORM_DATA,)
112
112
 
113
113
 
114
- def on_submit_string_event_spec() -> Tuple[Var[Dict[str, str]]]:
114
+ def on_submit_string_event_spec() -> Tuple[Var[dict[str, str]]]:
115
115
  """Event handler spec for the on_submit event.
116
116
 
117
117
  Returns:
@@ -153,7 +153,7 @@ class Form(BaseHTML):
153
153
  target: Var[Union[str, int, bool]]
154
154
 
155
155
  # If true, the form will be cleared after submit.
156
- reset_on_submit: Var[bool] = False # type: ignore
156
+ reset_on_submit: Var[bool] = Var.create(False)
157
157
 
158
158
  # The name used to make this form's submit handler function unique.
159
159
  handle_submit_unique_name: Var[str]
@@ -405,7 +405,7 @@ class Input(BaseHTML):
405
405
  (value_var := Var.create(value))._var_type
406
406
  ):
407
407
  props["value"] = ternary_operation(
408
- (value_var != Var.create(None)) # pyright: ignore [reportGeneralTypeIssues]
408
+ (value_var != Var.create(None)) # pyright: ignore [reportArgumentType]
409
409
  & (value_var != Var(_js_expr="undefined")),
410
410
  value,
411
411
  Var.create(""),
@@ -270,8 +270,8 @@ class Fieldset(Element):
270
270
  """
271
271
  ...
272
272
 
273
- def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]: ...
274
- def on_submit_string_event_spec() -> Tuple[Var[Dict[str, str]]]: ...
273
+ def on_submit_event_spec() -> Tuple[Var[dict[str, Any]]]: ...
274
+ def on_submit_string_event_spec() -> Tuple[Var[dict[str, str]]]: ...
275
275
 
276
276
  class Form(BaseHTML):
277
277
  @overload
@@ -341,10 +341,10 @@ class Form(BaseHTML):
341
341
  on_submit: Optional[
342
342
  Union[
343
343
  Union[
344
- EventType[[], BASE_STATE], EventType[[Dict[str, Any]], BASE_STATE]
344
+ EventType[[], BASE_STATE], EventType[[dict[str, Any]], BASE_STATE]
345
345
  ],
346
346
  Union[
347
- EventType[[], BASE_STATE], EventType[[Dict[str, str]], BASE_STATE]
347
+ EventType[[], BASE_STATE], EventType[[dict[str, str]], BASE_STATE]
348
348
  ],
349
349
  ]
350
350
  ] = None,
@@ -2,13 +2,15 @@
2
2
 
3
3
  from reflex.components.component import Component
4
4
  from reflex.utils import format
5
- from reflex.vars.base import Var
5
+ from reflex.utils.imports import ImportVar
6
+ from reflex.vars.base import LiteralVar, Var
7
+ from reflex.vars.sequence import LiteralStringVar
6
8
 
7
9
 
8
10
  class LucideIconComponent(Component):
9
11
  """Lucide Icon Component."""
10
12
 
11
- library = "lucide-react@0.469.0"
13
+ library = "lucide-react@0.471.1"
12
14
 
13
15
 
14
16
  class Icon(LucideIconComponent):
@@ -32,6 +34,7 @@ class Icon(LucideIconComponent):
32
34
  Raises:
33
35
  AttributeError: The errors tied to bad usage of the Icon component.
34
36
  ValueError: If the icon tag is invalid.
37
+ TypeError: If the icon name is not a string.
35
38
 
36
39
  Returns:
37
40
  The created component.
@@ -39,7 +42,6 @@ class Icon(LucideIconComponent):
39
42
  if children:
40
43
  if len(children) == 1 and isinstance(children[0], str):
41
44
  props["tag"] = children[0]
42
- children = []
43
45
  else:
44
46
  raise AttributeError(
45
47
  f"Passing multiple children to Icon component is not allowed: remove positional arguments {children[1:]} to fix"
@@ -47,19 +49,46 @@ class Icon(LucideIconComponent):
47
49
  if "tag" not in props:
48
50
  raise AttributeError("Missing 'tag' keyword-argument for Icon")
49
51
 
52
+ tag: str | Var | LiteralVar = props.pop("tag")
53
+ if isinstance(tag, LiteralVar):
54
+ if isinstance(tag, LiteralStringVar):
55
+ tag = tag._var_value
56
+ else:
57
+ raise TypeError(f"Icon name must be a string, got {type(tag)}")
58
+ elif isinstance(tag, Var):
59
+ return DynamicIcon.create(name=tag, **props)
60
+
50
61
  if (
51
- not isinstance(props["tag"], str)
52
- or format.to_snake_case(props["tag"]) not in LUCIDE_ICON_LIST
62
+ not isinstance(tag, str)
63
+ or format.to_snake_case(tag) not in LUCIDE_ICON_LIST
53
64
  ):
54
65
  raise ValueError(
55
- f"Invalid icon tag: {props['tag']}. Please use one of the following: {', '.join(LUCIDE_ICON_LIST[0:25])}, ..."
66
+ f"Invalid icon tag: {tag}. Please use one of the following: {', '.join(LUCIDE_ICON_LIST[0:25])}, ..."
56
67
  "\nSee full list at https://lucide.dev/icons."
57
68
  )
58
69
 
59
- props["tag"] = format.to_title_case(format.to_snake_case(props["tag"])) + "Icon"
70
+ if tag in LUCIDE_ICON_MAPPING_OVERRIDE:
71
+ props["tag"] = LUCIDE_ICON_MAPPING_OVERRIDE[tag]
72
+ else:
73
+ props["tag"] = format.to_title_case(format.to_snake_case(tag)) + "Icon"
60
74
  props["alias"] = f"Lucide{props['tag']}"
61
75
  props.setdefault("color", "var(--current-color)")
62
- return super().create(*children, **props)
76
+ return super().create(**props)
77
+
78
+
79
+ class DynamicIcon(LucideIconComponent):
80
+ """A DynamicIcon component."""
81
+
82
+ tag = "DynamicIcon"
83
+
84
+ name: Var[str]
85
+
86
+ def _get_imports(self):
87
+ _imports = super()._get_imports()
88
+ if self.library:
89
+ _imports.pop(self.library)
90
+ _imports["lucide-react/dynamic"] = [ImportVar("DynamicIcon", install=False)]
91
+ return _imports
63
92
 
64
93
 
65
94
  LUCIDE_ICON_LIST = [
@@ -841,6 +870,7 @@ LUCIDE_ICON_LIST = [
841
870
  "house",
842
871
  "house_plug",
843
872
  "house_plus",
873
+ "house_wifi",
844
874
  "ice_cream_bowl",
845
875
  "ice_cream_cone",
846
876
  "id_card",
@@ -1529,6 +1559,7 @@ LUCIDE_ICON_LIST = [
1529
1559
  "trending_up_down",
1530
1560
  "triangle",
1531
1561
  "triangle_alert",
1562
+ "triangle_dashed",
1532
1563
  "triangle_right",
1533
1564
  "trophy",
1534
1565
  "truck",
@@ -1634,3 +1665,10 @@ LUCIDE_ICON_LIST = [
1634
1665
  "zoom_in",
1635
1666
  "zoom_out",
1636
1667
  ]
1668
+
1669
+ # The default transformation of some icon names doesn't match how the
1670
+ # icons are exported from Lucide. Manual overrides can go here.
1671
+ LUCIDE_ICON_MAPPING_OVERRIDE = {
1672
+ "grid_2x_2_check": "Grid2x2Check",
1673
+ "grid_2x_2_x": "Grid2x2X",
1674
+ }
@@ -104,12 +104,60 @@ class Icon(LucideIconComponent):
104
104
  Raises:
105
105
  AttributeError: The errors tied to bad usage of the Icon component.
106
106
  ValueError: If the icon tag is invalid.
107
+ TypeError: If the icon name is not a string.
107
108
 
108
109
  Returns:
109
110
  The created component.
110
111
  """
111
112
  ...
112
113
 
114
+ class DynamicIcon(LucideIconComponent):
115
+ @overload
116
+ @classmethod
117
+ def create( # type: ignore
118
+ cls,
119
+ *children,
120
+ name: Optional[Union[Var[str], str]] = None,
121
+ style: Optional[Style] = None,
122
+ key: Optional[Any] = None,
123
+ id: Optional[Any] = None,
124
+ class_name: Optional[Any] = None,
125
+ autofocus: Optional[bool] = None,
126
+ custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
127
+ on_blur: Optional[EventType[[], BASE_STATE]] = None,
128
+ on_click: Optional[EventType[[], BASE_STATE]] = None,
129
+ on_context_menu: Optional[EventType[[], BASE_STATE]] = None,
130
+ on_double_click: Optional[EventType[[], BASE_STATE]] = None,
131
+ on_focus: Optional[EventType[[], BASE_STATE]] = None,
132
+ on_mount: Optional[EventType[[], BASE_STATE]] = None,
133
+ on_mouse_down: Optional[EventType[[], BASE_STATE]] = None,
134
+ on_mouse_enter: Optional[EventType[[], BASE_STATE]] = None,
135
+ on_mouse_leave: Optional[EventType[[], BASE_STATE]] = None,
136
+ on_mouse_move: Optional[EventType[[], BASE_STATE]] = None,
137
+ on_mouse_out: Optional[EventType[[], BASE_STATE]] = None,
138
+ on_mouse_over: Optional[EventType[[], BASE_STATE]] = None,
139
+ on_mouse_up: Optional[EventType[[], BASE_STATE]] = None,
140
+ on_scroll: Optional[EventType[[], BASE_STATE]] = None,
141
+ on_unmount: Optional[EventType[[], BASE_STATE]] = None,
142
+ **props,
143
+ ) -> "DynamicIcon":
144
+ """Create the component.
145
+
146
+ Args:
147
+ *children: The children of the component.
148
+ style: The style of the component.
149
+ key: A unique key for the component.
150
+ id: The id for the component.
151
+ class_name: The class name for the component.
152
+ autofocus: Whether the component should take the focus once the page is loaded
153
+ custom_attrs: custom attribute
154
+ **props: The props of the component.
155
+
156
+ Returns:
157
+ The component.
158
+ """
159
+ ...
160
+
113
161
  LUCIDE_ICON_LIST = [
114
162
  "a_arrow_down",
115
163
  "a_arrow_up",
@@ -889,6 +937,7 @@ LUCIDE_ICON_LIST = [
889
937
  "house",
890
938
  "house_plug",
891
939
  "house_plus",
940
+ "house_wifi",
892
941
  "ice_cream_bowl",
893
942
  "ice_cream_cone",
894
943
  "id_card",
@@ -1577,6 +1626,7 @@ LUCIDE_ICON_LIST = [
1577
1626
  "trending_up_down",
1578
1627
  "triangle",
1579
1628
  "triangle_alert",
1629
+ "triangle_dashed",
1580
1630
  "triangle_right",
1581
1631
  "trophy",
1582
1632
  "truck",
@@ -1682,3 +1732,7 @@ LUCIDE_ICON_LIST = [
1682
1732
  "zoom_in",
1683
1733
  "zoom_out",
1684
1734
  ]
1735
+ LUCIDE_ICON_MAPPING_OVERRIDE = {
1736
+ "grid_2x_2_check": "Grid2x2Check",
1737
+ "grid_2x_2_x": "Grid2x2X",
1738
+ }
@@ -8,7 +8,7 @@ from functools import lru_cache
8
8
  from hashlib import md5
9
9
  from typing import Any, Callable, Dict, Sequence, Union
10
10
 
11
- from reflex.components.component import Component, CustomComponent
11
+ from reflex.components.component import BaseComponent, Component, CustomComponent
12
12
  from reflex.components.tags.tag import Tag
13
13
  from reflex.utils import types
14
14
  from reflex.utils.imports import ImportDict, ImportVar
@@ -65,8 +65,8 @@ def get_base_component_map() -> dict[str, Callable]:
65
65
  "h5": lambda value: Heading.create(value, as_="h5", size="2", margin_y="0.5em"),
66
66
  "h6": lambda value: Heading.create(value, as_="h6", size="1", margin_y="0.5em"),
67
67
  "p": lambda value: Text.create(value, margin_y="1em"),
68
- "ul": lambda value: UnorderedList.create(value, margin_y="1em"), # type: ignore
69
- "ol": lambda value: OrderedList.create(value, margin_y="1em"), # type: ignore
68
+ "ul": lambda value: UnorderedList.create(value, margin_y="1em"),
69
+ "ol": lambda value: OrderedList.create(value, margin_y="1em"),
70
70
  "li": lambda value: ListItem.create(value, margin_y="0.5em"),
71
71
  "a": lambda value: Link.create(value),
72
72
  "code": lambda value: Code.create(value),
@@ -236,7 +236,7 @@ class Markdown(Component):
236
236
  ),
237
237
  },
238
238
  *[
239
- component(_MOCK_ARG)._get_all_imports() # type: ignore
239
+ component(_MOCK_ARG)._get_all_imports()
240
240
  for component in self.component_map.values()
241
241
  ],
242
242
  ]
@@ -327,7 +327,7 @@ const {_LANGUAGE!s} = match ? match[1] : '';
327
327
  if tag != "codeblock"
328
328
  # For codeblock, the mapping for some cases returns an array of elements. Let's join them into a string.
329
329
  else ternary_operation(
330
- ARRAY_ISARRAY.call(_CHILDREN), # type: ignore
330
+ ARRAY_ISARRAY.call(_CHILDREN), # pyright: ignore [reportArgumentType]
331
331
  _CHILDREN.to(list).join("\n"),
332
332
  _CHILDREN,
333
333
  ).to(str)
@@ -379,7 +379,9 @@ const {_LANGUAGE!s} = match ? match[1] : '';
379
379
  # fallback to the default fn Var creation if the component is not a MarkdownComponentMap.
380
380
  return MarkdownComponentMap.create_map_fn_var(fn_body=formatted_component)
381
381
 
382
- def _get_map_fn_custom_code_from_children(self, component) -> list[str]:
382
+ def _get_map_fn_custom_code_from_children(
383
+ self, component: BaseComponent
384
+ ) -> list[str]:
383
385
  """Recursively get markdown custom code from children components.
384
386
 
385
387
  Args:
@@ -409,7 +411,7 @@ const {_LANGUAGE!s} = match ? match[1] : '';
409
411
  return custom_code_list
410
412
 
411
413
  @staticmethod
412
- def _component_map_hash(component_map) -> str:
414
+ def _component_map_hash(component_map: dict) -> str:
413
415
  inp = str(
414
416
  {tag: component(_MOCK_ARG) for tag, component in component_map.items()}
415
417
  ).encode()
@@ -425,7 +427,7 @@ const {_LANGUAGE!s} = match ? match[1] : '';
425
427
  for _component in self.component_map.values():
426
428
  comp = _component(_MOCK_ARG)
427
429
  hooks.update(comp._get_all_hooks())
428
- formatted_hooks = MACROS.module.renderHooks(hooks) # type: ignore
430
+ formatted_hooks = MACROS.module.renderHooks(hooks) # pyright: ignore [reportAttributeAccessIssue]
429
431
  return f"""
430
432
  function {self._get_component_map_name()} () {{
431
433
  {formatted_hooks}
@@ -28,9 +28,9 @@ class MomentDelta:
28
28
  class Moment(NoSSRComponent):
29
29
  """The Moment component."""
30
30
 
31
- tag: str = "Moment"
31
+ tag: str | None = "Moment"
32
32
  is_default = True
33
- library: str = "react-moment"
33
+ library: str | None = "react-moment"
34
34
  lib_dependencies: List[str] = ["moment"]
35
35
 
36
36
  # How often the date update (how often time update / 0 to disable).
@@ -1,13 +1,17 @@
1
1
  """Image component from next/image."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from typing import Any, Literal, Optional, Union
4
6
 
5
7
  from reflex.event import EventHandler, no_args_event_spec
6
- from reflex.utils import types
8
+ from reflex.utils import console, types
7
9
  from reflex.vars.base import Var
8
10
 
9
11
  from .base import NextComponent
10
12
 
13
+ DEFAULT_W_H = "100%"
14
+
11
15
 
12
16
  class Image(NextComponent):
13
17
  """Display an image."""
@@ -53,7 +57,7 @@ class Image(NextComponent):
53
57
  loading: Var[Literal["lazy", "eager"]]
54
58
 
55
59
  # A Data URL to be used as a placeholder image before the src image successfully loads. Only takes effect when combined with placeholder="blur".
56
- blurDataURL: Var[str]
60
+ blur_data_url: Var[str]
57
61
 
58
62
  # Fires when the image has loaded.
59
63
  on_load: EventHandler[no_args_event_spec]
@@ -80,10 +84,18 @@ class Image(NextComponent):
80
84
  Returns:
81
85
  _type_: _description_
82
86
  """
87
+ if "blurDataURL" in props:
88
+ console.deprecate(
89
+ feature_name="blurDataURL",
90
+ reason="Use blur_data_url instead",
91
+ deprecation_version="0.7.0",
92
+ removal_version="0.8.0",
93
+ )
94
+ props["blur_data_url"] = props.pop("blurDataURL")
95
+
83
96
  style = props.get("style", {})
84
- DEFAULT_W_H = "100%"
85
97
 
86
- def check_prop_type(prop_name, prop_value):
98
+ def check_prop_type(prop_name: str, prop_value: int | str | None):
87
99
  if types.check_prop_in_allowed_types(prop_value, allowed_types=[int]):
88
100
  props[prop_name] = prop_value
89
101