reflex 0.4.4a1__py3-none-any.whl → 0.4.5__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.

reflex/compiler/utils.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Common utility functions used in the compiler."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import os
@@ -255,7 +256,7 @@ def compile_custom_component(
255
256
  "name": component.tag,
256
257
  "props": props,
257
258
  "render": render.render(),
258
- "hooks": render.get_hooks(),
259
+ "hooks": render.get_hooks_internal() | render.get_hooks(),
259
260
  "custom_code": render.get_custom_code(),
260
261
  },
261
262
  imports,
@@ -404,7 +405,11 @@ def get_asset_path(filename: str | None = None) -> str:
404
405
 
405
406
 
406
407
  def add_meta(
407
- page: Component, title: str, image: str, description: str, meta: list[dict]
408
+ page: Component,
409
+ title: str,
410
+ image: str,
411
+ meta: list[dict],
412
+ description: str | None = None,
408
413
  ) -> Component:
409
414
  """Add metadata to a page.
410
415
 
@@ -412,19 +417,24 @@ def add_meta(
412
417
  page: The component for the page.
413
418
  title: The title of the page.
414
419
  image: The image for the page.
415
- description: The description of the page.
416
420
  meta: The metadata list.
421
+ description: The description of the page.
417
422
 
418
423
  Returns:
419
424
  The component with the metadata added.
420
425
  """
421
426
  meta_tags = [Meta.create(**item) for item in meta]
422
427
 
428
+ children: list[Any] = [
429
+ Title.create(title),
430
+ ]
431
+ if description:
432
+ children.append(Description.create(content=description))
433
+ children.append(Image.create(content=image))
434
+
423
435
  page.children.append(
424
436
  Head.create(
425
- Title.create(title),
426
- Description.create(content=description),
427
- Image.create(content=image),
437
+ *children,
428
438
  *meta_tags,
429
439
  )
430
440
  )
@@ -74,6 +74,14 @@ class BaseComponent(Base, ABC):
74
74
  The dictionary for template of the component.
75
75
  """
76
76
 
77
+ @abstractmethod
78
+ def get_hooks_internal(self) -> set[str]:
79
+ """Get the reflex internal hooks for the component and its children.
80
+
81
+ Returns:
82
+ The code that should appear just before user-defined hooks.
83
+ """
84
+
77
85
  @abstractmethod
78
86
  def get_hooks(self) -> set[str]:
79
87
  """Get the React hooks for this component.
@@ -873,6 +881,20 @@ class Component(BaseComponent, ABC):
873
881
 
874
882
  return vars
875
883
 
884
+ def _has_event_triggers(self) -> bool:
885
+ """Check if the component or children have any event triggers.
886
+
887
+ Returns:
888
+ True if the component or children have any event triggers.
889
+ """
890
+ if self.event_triggers:
891
+ return True
892
+ else:
893
+ for child in self.children:
894
+ if isinstance(child, Component) and child._has_event_triggers():
895
+ return True
896
+ return False
897
+
876
898
  def _get_custom_code(self) -> str | None:
877
899
  """Get custom code for the component.
878
900
 
@@ -1127,14 +1149,28 @@ class Component(BaseComponent, ABC):
1127
1149
  """
1128
1150
  return
1129
1151
 
1152
+ def get_hooks_internal(self) -> set[str]:
1153
+ """Get the reflex internal hooks for the component and its children.
1154
+
1155
+ Returns:
1156
+ The code that should appear just before user-defined hooks.
1157
+ """
1158
+ # Store the code in a set to avoid duplicates.
1159
+ code = self._get_hooks_internal()
1160
+
1161
+ # Add the hook code for the children.
1162
+ for child in self.children:
1163
+ code |= child.get_hooks_internal()
1164
+
1165
+ return code
1166
+
1130
1167
  def get_hooks(self) -> Set[str]:
1131
1168
  """Get the React hooks for this component and its children.
1132
1169
 
1133
1170
  Returns:
1134
1171
  The code that should appear just before returning the rendered component.
1135
1172
  """
1136
- # Store the code in a set to avoid duplicates.
1137
- code = self._get_hooks_internal()
1173
+ code = set()
1138
1174
 
1139
1175
  # Add the hook code for this component.
1140
1176
  hooks = self._get_hooks()
@@ -1251,6 +1287,9 @@ class CustomComponent(Component):
1251
1287
  # The props of the component.
1252
1288
  props: Dict[str, Any] = {}
1253
1289
 
1290
+ # Props that reference other components.
1291
+ component_props: Dict[str, Component] = {}
1292
+
1254
1293
  def __init__(self, *args, **kwargs):
1255
1294
  """Initialize the custom component.
1256
1295
 
@@ -1282,19 +1321,21 @@ class CustomComponent(Component):
1282
1321
  self.props[format.to_camel_case(key)] = value
1283
1322
  continue
1284
1323
 
1285
- # Convert the type to a Var, then get the type of the var.
1286
- if not types._issubclass(type_, Var):
1287
- type_ = Var[type_]
1288
- type_ = types.get_args(type_)[0]
1289
-
1290
1324
  # Handle subclasses of Base.
1291
- if types._issubclass(type_, Base):
1292
- try:
1293
- value = BaseVar(
1294
- _var_name=value.json(), _var_type=type_, _var_is_local=True
1325
+ if isinstance(value, Base):
1326
+ base_value = Var.create(value)
1327
+
1328
+ # Track hooks and imports associated with Component instances.
1329
+ if base_value is not None and isinstance(value, Component):
1330
+ self.component_props[key] = value
1331
+ value = base_value._replace(
1332
+ merge_var_data=VarData( # type: ignore
1333
+ imports=value.get_imports(),
1334
+ hooks=value.get_hooks(),
1335
+ )
1295
1336
  )
1296
- except Exception:
1297
- value = Var.create(value)
1337
+ else:
1338
+ value = base_value
1298
1339
  else:
1299
1340
  value = Var.create(value, _var_is_string=type(value) is str)
1300
1341
 
@@ -1353,6 +1394,16 @@ class CustomComponent(Component):
1353
1394
  custom_components |= self.get_component(self).get_custom_components(
1354
1395
  seen=seen
1355
1396
  )
1397
+
1398
+ # Fetch custom components from props as well.
1399
+ for child_component in self.component_props.values():
1400
+ if child_component.tag is None:
1401
+ continue
1402
+ if child_component.tag not in seen:
1403
+ seen.add(child_component.tag)
1404
+ if isinstance(child_component, CustomComponent):
1405
+ custom_components |= {child_component}
1406
+ custom_components |= child_component.get_custom_components(seen=seen)
1356
1407
  return custom_components
1357
1408
 
1358
1409
  def _render(self) -> Tag:
@@ -1379,6 +1430,19 @@ class CustomComponent(Component):
1379
1430
  for name, prop in self.props.items()
1380
1431
  ]
1381
1432
 
1433
+ def _get_vars(self, include_children: bool = False) -> list[Var]:
1434
+ """Walk all Vars used in this component.
1435
+
1436
+ Args:
1437
+ include_children: Whether to include Vars from children.
1438
+
1439
+ Returns:
1440
+ Each var referenced by the component (props, styles, event handlers).
1441
+ """
1442
+ return super()._get_vars(include_children=include_children) + [
1443
+ prop for prop in self.props.values() if isinstance(prop, Var)
1444
+ ]
1445
+
1382
1446
  @lru_cache(maxsize=None) # noqa
1383
1447
  def get_component(self) -> Component:
1384
1448
  """Render the component.
@@ -1743,6 +1807,14 @@ class StatefulComponent(BaseComponent):
1743
1807
  )
1744
1808
  return trigger_memo
1745
1809
 
1810
+ def get_hooks_internal(self) -> set[str]:
1811
+ """Get the reflex internal hooks for the component and its children.
1812
+
1813
+ Returns:
1814
+ The code that should appear just before user-defined hooks.
1815
+ """
1816
+ return set()
1817
+
1746
1818
  def get_hooks(self) -> set[str]:
1747
1819
  """Get the React hooks for this component.
1748
1820
 
@@ -1851,7 +1923,7 @@ class MemoizationLeaf(Component):
1851
1923
  The memoization leaf
1852
1924
  """
1853
1925
  comp = super().create(*children, **props)
1854
- if comp.get_hooks():
1926
+ if comp.get_hooks() or comp.get_hooks_internal():
1855
1927
  comp._memoization_mode = cls._memoization_mode.copy(
1856
1928
  update={"disposition": MemoizationDisposition.ALWAYS}
1857
1929
  )
@@ -128,7 +128,7 @@ uploaded_files_url_prefix: Var = Var.create_safe(
128
128
  )
129
129
 
130
130
 
131
- def get_upload_url(file_path: str) -> str:
131
+ def get_upload_url(file_path: str) -> Var[str]:
132
132
  """Get the URL of an uploaded file.
133
133
 
134
134
  Args:
@@ -139,7 +139,7 @@ def get_upload_url(file_path: str) -> str:
139
139
  """
140
140
  Upload.is_used = True
141
141
 
142
- return f"{uploaded_files_url_prefix}/{file_path}"
142
+ return Var.create_safe(f"{uploaded_files_url_prefix}/{file_path}")
143
143
 
144
144
 
145
145
  def _on_drop_spec(files: Var):
@@ -40,7 +40,7 @@ def get_upload_dir() -> Path: ...
40
40
 
41
41
  uploaded_files_url_prefix: Var
42
42
 
43
- def get_upload_url(file_path: str) -> str: ...
43
+ def get_upload_url(file_path: str) -> Var[str]: ...
44
44
 
45
45
  class UploadFilesProvider(Component):
46
46
  @overload
@@ -162,7 +162,7 @@ class Form(BaseHTML):
162
162
  props["handle_submit_unique_name"] = ""
163
163
  form = super().create(*children, **props)
164
164
  form.handle_submit_unique_name = md5(
165
- str(form.get_hooks()).encode("utf-8")
165
+ str(form.get_hooks_internal().union(form.get_hooks())).encode("utf-8")
166
166
  ).hexdigest()
167
167
  return form
168
168
 
@@ -291,8 +291,10 @@ class Markdown(Component):
291
291
 
292
292
  def _get_custom_code(self) -> str | None:
293
293
  hooks = set()
294
- for component in self.component_map.values():
295
- hooks |= component(_MOCK_ARG).get_hooks()
294
+ for _component in self.component_map.values():
295
+ comp = _component(_MOCK_ARG)
296
+ hooks |= comp.get_hooks_internal()
297
+ hooks |= comp.get_hooks()
296
298
  formatted_hooks = "\n".join(hooks)
297
299
  return f"""
298
300
  function {self._get_component_map_name()} () {{
@@ -1,4 +1,5 @@
1
1
  """Interactive components provided by @radix-ui/themes."""
2
+ from __future__ import annotations
2
3
 
3
4
  from typing import Any, Dict, List, Literal, Optional, Union
4
5
 
@@ -126,7 +127,7 @@ class HighLevelRadioGroup(RadixThemesComponent):
126
127
  def create(
127
128
  cls,
128
129
  items: Var[List[Optional[Union[str, int, float, list, dict, bool]]]],
129
- **props
130
+ **props,
130
131
  ) -> Component:
131
132
  """Create a radio group component.
132
133
 
@@ -184,6 +184,9 @@ class HighLevelSelect(SelectRoot):
184
184
  # The width of the select.
185
185
  width: Var[str]
186
186
 
187
+ # The positioning mode to use. Default is "item-aligned".
188
+ position: Var[Literal["item-aligned", "popper"]]
189
+
187
190
  @classmethod
188
191
  def create(cls, items: Union[List[str], Var[List[str]]], **props) -> Component:
189
192
  """Create a select component.
@@ -196,12 +199,14 @@ class HighLevelSelect(SelectRoot):
196
199
  The select component.
197
200
  """
198
201
  content_props = {
199
- prop: props.pop(prop) for prop in ["high_contrast"] if prop in props
202
+ prop: props.pop(prop)
203
+ for prop in ["high_contrast", "position"]
204
+ if prop in props
200
205
  }
201
206
 
202
207
  trigger_props = {
203
208
  prop: props.pop(prop)
204
- for prop in ["placeholder", "variant", "radius", "width"]
209
+ for prop in ["placeholder", "variant", "radius", "width", "flex_shrink"]
205
210
  if prop in props
206
211
  }
207
212
 
@@ -864,6 +864,12 @@ class HighLevelSelect(SelectRoot):
864
864
  ]
865
865
  ] = None,
866
866
  width: Optional[Union[Var[str], str]] = None,
867
+ position: Optional[
868
+ Union[
869
+ Var[Literal["item-aligned", "popper"]],
870
+ Literal["item-aligned", "popper"],
871
+ ]
872
+ ] = None,
867
873
  size: Optional[
868
874
  Union[Var[Literal["1", "2", "3"]], Literal["1", "2", "3"]]
869
875
  ] = None,
@@ -945,6 +951,7 @@ class HighLevelSelect(SelectRoot):
945
951
  variant: The variant of the select.
946
952
  radius: The radius of the select.
947
953
  width: The width of the select.
954
+ position: The positioning mode to use. Default is "item-aligned".
948
955
  size: The size of the select: "1" | "2" | "3"
949
956
  default_value: The value of the select when initially rendered. Use when you do not need to control the state of the select.
950
957
  value: The controlled value of the select. Should be used in conjunction with on_change.
@@ -1057,6 +1064,12 @@ class Select(ComponentNamespace):
1057
1064
  ]
1058
1065
  ] = None,
1059
1066
  width: Optional[Union[Var[str], str]] = None,
1067
+ position: Optional[
1068
+ Union[
1069
+ Var[Literal["item-aligned", "popper"]],
1070
+ Literal["item-aligned", "popper"],
1071
+ ]
1072
+ ] = None,
1060
1073
  size: Optional[
1061
1074
  Union[Var[Literal["1", "2", "3"]], Literal["1", "2", "3"]]
1062
1075
  ] = None,
@@ -1138,6 +1151,7 @@ class Select(ComponentNamespace):
1138
1151
  variant: The variant of the select.
1139
1152
  radius: The radius of the select.
1140
1153
  width: The width of the select.
1154
+ position: The positioning mode to use. Default is "item-aligned".
1141
1155
  size: The size of the select: "1" | "2" | "3"
1142
1156
  default_value: The value of the select when initially rendered. Use when you do not need to control the state of the select.
1143
1157
  value: The controlled value of the select. Should be used in conjunction with on_change.
@@ -5,6 +5,8 @@ from typing import Iterable, Literal, Optional, Union
5
5
  from reflex.components.component import Component, ComponentNamespace
6
6
  from reflex.components.core.foreach import Foreach
7
7
  from reflex.components.el.elements.typography import Li
8
+ from reflex.components.lucide.icon import Icon
9
+ from reflex.components.radix.themes.typography.text import Text
8
10
  from reflex.style import Style
9
11
  from reflex.vars import Var
10
12
 
@@ -39,12 +41,16 @@ LiteralListStyleTypeOrdered = Literal[
39
41
  class BaseList(Flex, LayoutComponent):
40
42
  """Base class for ordered and unordered lists."""
41
43
 
44
+ # The style of the list. Default to "none".
45
+ list_style_type: Var[
46
+ Union[LiteralListStyleTypeUnordered, LiteralListStyleTypeOrdered]
47
+ ]
48
+
42
49
  @classmethod
43
50
  def create(
44
51
  cls,
45
52
  *children,
46
53
  items: Optional[Union[Var[Iterable], Iterable]] = None,
47
- list_style_type: str = "",
48
54
  **props,
49
55
  ):
50
56
  """Create a list component.
@@ -52,21 +58,23 @@ class BaseList(Flex, LayoutComponent):
52
58
  Args:
53
59
  *children: The children of the component.
54
60
  items: A list of items to add to the list.
55
- list_style_type: The style of the list.
56
61
  **props: The properties of the component.
57
62
 
58
63
  Returns:
59
64
  The list component.
65
+
60
66
  """
67
+ list_style_type = props.pop("list_style_type", "none")
61
68
  if not children and items is not None:
62
69
  if isinstance(items, Var):
63
70
  children = [Foreach.create(items, ListItem.create)]
64
71
  else:
65
72
  children = [ListItem.create(item) for item in items]
66
- props["list_style_type"] = list_style_type
73
+ # props["list_style_type"] = list_style_type
67
74
  props["direction"] = "column"
68
75
  style = props.setdefault("style", {})
69
76
  style["list_style_position"] = "outside"
77
+ style["list_style_type"] = list_style_type
70
78
  if "gap" in props:
71
79
  style["gap"] = props["gap"]
72
80
  return super().create(*children, **props)
@@ -102,6 +110,7 @@ class UnorderedList(BaseList):
102
110
 
103
111
  Returns:
104
112
  The list component.
113
+
105
114
  """
106
115
  return super().create(
107
116
  *children, items=items, list_style_type=list_style_type, **props
@@ -129,6 +138,7 @@ class OrderedList(BaseList):
129
138
 
130
139
  Returns:
131
140
  The list component.
141
+
132
142
  """
133
143
  return super().create(
134
144
  *children, items=items, list_style_type=list_style_type, **props
@@ -138,7 +148,24 @@ class OrderedList(BaseList):
138
148
  class ListItem(Li):
139
149
  """Display an item of an ordered or unordered list."""
140
150
 
141
- ...
151
+ @classmethod
152
+ def create(cls, *children, **props):
153
+ """Create a list item component.
154
+
155
+ Args:
156
+ *children: The children of the component.
157
+ **props: The properties of the component.
158
+
159
+ Returns:
160
+ The list item component.
161
+
162
+ """
163
+ for child in children:
164
+ if isinstance(child, Text):
165
+ child.as_ = "span"
166
+ elif isinstance(child, Icon) and "display" not in child.style:
167
+ child.style["display"] = "inline"
168
+ return super().create(*children, **props)
142
169
 
143
170
 
144
171
  class List(ComponentNamespace):
@@ -11,6 +11,8 @@ from typing import Iterable, Literal, Optional, Union
11
11
  from reflex.components.component import Component, ComponentNamespace
12
12
  from reflex.components.core.foreach import Foreach
13
13
  from reflex.components.el.elements.typography import Li
14
+ from reflex.components.lucide.icon import Icon
15
+ from reflex.components.radix.themes.typography.text import Text
14
16
  from reflex.style import Style
15
17
  from reflex.vars import Var
16
18
  from .base import LayoutComponent
@@ -41,7 +43,50 @@ class BaseList(Flex, LayoutComponent):
41
43
  cls,
42
44
  *children,
43
45
  items: Optional[Union[Union[Var[Iterable], Iterable], Iterable]] = None,
44
- list_style_type: Optional[str] = "",
46
+ list_style_type: Optional[
47
+ Union[
48
+ Var[
49
+ Union[
50
+ Literal["none", "disc", "circle", "square"],
51
+ Literal[
52
+ "none",
53
+ "decimal",
54
+ "decimal-leading-zero",
55
+ "lower-roman",
56
+ "upper-roman",
57
+ "lower-greek",
58
+ "lower-latin",
59
+ "upper-latin",
60
+ "armenian",
61
+ "georgian",
62
+ "lower-alpha",
63
+ "upper-alpha",
64
+ "hiragana",
65
+ "katakana",
66
+ ],
67
+ ]
68
+ ],
69
+ Union[
70
+ Literal["none", "disc", "circle", "square"],
71
+ Literal[
72
+ "none",
73
+ "decimal",
74
+ "decimal-leading-zero",
75
+ "lower-roman",
76
+ "upper-roman",
77
+ "lower-greek",
78
+ "lower-latin",
79
+ "upper-latin",
80
+ "armenian",
81
+ "georgian",
82
+ "lower-alpha",
83
+ "upper-alpha",
84
+ "hiragana",
85
+ "katakana",
86
+ ],
87
+ ],
88
+ ]
89
+ ] = None,
45
90
  as_child: Optional[Union[Var[bool], bool]] = None,
46
91
  direction: Optional[
47
92
  Union[
@@ -257,7 +302,7 @@ class BaseList(Flex, LayoutComponent):
257
302
  Args:
258
303
  *children: The children of the component.
259
304
  items: A list of items to add to the list.
260
- list_style_type: The style of the list.
305
+ list_style_type: The style of the list. Default to "none".
261
306
  as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
262
307
  direction: How child items are layed out: "row" | "column" | "row-reverse" | "column-reverse"
263
308
  align: Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch"
@@ -306,6 +351,7 @@ class BaseList(Flex, LayoutComponent):
306
351
 
307
352
  Returns:
308
353
  The list component.
354
+
309
355
  """
310
356
  ...
311
357
 
@@ -581,6 +627,7 @@ class UnorderedList(BaseList):
581
627
 
582
628
  Returns:
583
629
  The list component.
630
+
584
631
  """
585
632
  ...
586
633
 
@@ -873,12 +920,11 @@ class OrderedList(BaseList):
873
920
 
874
921
  Returns:
875
922
  The list component.
923
+
876
924
  """
877
925
  ...
878
926
 
879
927
  class ListItem(Li):
880
- ...
881
-
882
928
  @overload
883
929
  @classmethod
884
930
  def create( # type: ignore
@@ -977,7 +1023,7 @@ class ListItem(Li):
977
1023
  ] = None,
978
1024
  **props
979
1025
  ) -> "ListItem":
980
- """Create the component.
1026
+ """Create a list item component.
981
1027
 
982
1028
  Args:
983
1029
  *children: The children of the component.
@@ -1003,13 +1049,11 @@ class ListItem(Li):
1003
1049
  class_name: The class name for the component.
1004
1050
  autofocus: Whether the component should take the focus once the page is loaded
1005
1051
  custom_attrs: custom attribute
1006
- **props: The props of the component.
1052
+ **props: The properties of the component.
1007
1053
 
1008
1054
  Returns:
1009
- The component.
1055
+ The list item component.
1010
1056
 
1011
- Raises:
1012
- TypeError: If an invalid child is passed.
1013
1057
  """
1014
1058
  ...
1015
1059
 
@@ -71,7 +71,7 @@ class Node(SimpleNamespace):
71
71
  # The Node version.
72
72
  VERSION = "18.17.0"
73
73
  # The minimum required node version.
74
- MIN_VERSION = "16.8.0"
74
+ MIN_VERSION = "18.17.0"
75
75
 
76
76
  # The node bin path.
77
77
  BIN_PATH = os.path.join(
reflex/constants/route.py CHANGED
@@ -50,9 +50,9 @@ class DefaultPage(SimpleNamespace):
50
50
  """Default page constants."""
51
51
 
52
52
  # The default title to show for Reflex apps.
53
- TITLE = "Reflex App"
53
+ TITLE = "{} | {}"
54
54
  # The default description to show for Reflex apps.
55
- DESCRIPTION = "A Reflex app."
55
+ DESCRIPTION = ""
56
56
  # The default image to show for Reflex apps.
57
57
  IMAGE = "favicon.ico"
58
58
  # The default meta list to show for Reflex apps.
reflex/event.py CHANGED
@@ -147,6 +147,10 @@ class EventHandler(EventActionsMixin):
147
147
  # The function to call in response to the event.
148
148
  fn: Any
149
149
 
150
+ # The full name of the state class this event handler is attached to.
151
+ # Emtpy string means this event handler is a server side event.
152
+ state_full_name: str = ""
153
+
150
154
  class Config:
151
155
  """The Pydantic config."""
152
156