reflex 0.5.7a1__py3-none-any.whl → 0.5.8a1__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 (44) hide show
  1. reflex/.templates/web/utils/state.js +1 -1
  2. reflex/app.py +20 -2
  3. reflex/components/core/banner.py +14 -0
  4. reflex/components/core/banner.pyi +3 -3
  5. reflex/components/core/debounce.py +3 -0
  6. reflex/components/el/__init__.py +1 -0
  7. reflex/components/el/__init__.pyi +8 -5
  8. reflex/components/el/elements/__init__.py +3 -1
  9. reflex/components/el/elements/__init__.pyi +8 -6
  10. reflex/components/el/elements/media.py +98 -18
  11. reflex/components/el/elements/media.pyi +523 -18
  12. reflex/components/el/elements/metadata.py +5 -1
  13. reflex/components/radix/primitives/base.py +1 -1
  14. reflex/components/radix/themes/layout/list.py +0 -2
  15. reflex/components/recharts/cartesian.py +46 -20
  16. reflex/components/recharts/cartesian.pyi +26 -14
  17. reflex/components/recharts/charts.py +4 -0
  18. reflex/components/recharts/charts.pyi +3 -0
  19. reflex/components/recharts/general.py +23 -9
  20. reflex/components/recharts/general.pyi +6 -4
  21. reflex/components/recharts/polar.py +35 -11
  22. reflex/components/recharts/polar.pyi +35 -7
  23. reflex/components/sonner/toast.py +28 -2
  24. reflex/components/sonner/toast.pyi +14 -7
  25. reflex/constants/base.py +21 -0
  26. reflex/constants/event.py +2 -0
  27. reflex/experimental/vars/__init__.py +17 -0
  28. reflex/experimental/vars/base.py +282 -15
  29. reflex/experimental/vars/function.py +214 -0
  30. reflex/experimental/vars/number.py +1295 -0
  31. reflex/experimental/vars/sequence.py +1039 -0
  32. reflex/reflex.py +29 -2
  33. reflex/state.py +59 -10
  34. reflex/utils/imports.py +71 -8
  35. reflex/utils/prerequisites.py +115 -35
  36. reflex/utils/pyi_generator.py +2 -0
  37. reflex/utils/redir.py +52 -0
  38. reflex/vars.py +220 -11
  39. reflex/vars.pyi +20 -2
  40. {reflex-0.5.7a1.dist-info → reflex-0.5.8a1.dist-info}/METADATA +2 -2
  41. {reflex-0.5.7a1.dist-info → reflex-0.5.8a1.dist-info}/RECORD +44 -40
  42. {reflex-0.5.7a1.dist-info → reflex-0.5.8a1.dist-info}/LICENSE +0 -0
  43. {reflex-0.5.7a1.dist-info → reflex-0.5.8a1.dist-info}/WHEEL +0 -0
  44. {reflex-0.5.7a1.dist-info → reflex-0.5.8a1.dist-info}/entry_points.txt +0 -0
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Literal, Optional, Union
5
+ from typing import Any, ClassVar, Literal, Optional, Union
6
6
 
7
7
  from reflex.base import Base
8
8
  from reflex.components.component import Component, ComponentNamespace
@@ -211,6 +211,9 @@ class Toaster(Component):
211
211
  # Pauses toast timers when the page is hidden, e.g., when the tab is backgrounded, the browser is minimized, or the OS is locked.
212
212
  pause_when_page_is_hidden: Var[bool]
213
213
 
214
+ # Marked True when any Toast component is created.
215
+ is_used: ClassVar[bool] = False
216
+
214
217
  def add_hooks(self) -> list[Var | str]:
215
218
  """Add hooks for the toaster component.
216
219
 
@@ -231,7 +234,7 @@ class Toaster(Component):
231
234
  return [hook]
232
235
 
233
236
  @staticmethod
234
- def send_toast(message: str, level: str | None = None, **props) -> EventSpec:
237
+ def send_toast(message: str = "", level: str | None = None, **props) -> EventSpec:
235
238
  """Send a toast message.
236
239
 
237
240
  Args:
@@ -239,10 +242,19 @@ class Toaster(Component):
239
242
  level: The level of the toast.
240
243
  **props: The options for the toast.
241
244
 
245
+ Raises:
246
+ ValueError: If the Toaster component is not created.
247
+
242
248
  Returns:
243
249
  The toast event.
244
250
  """
251
+ if not Toaster.is_used:
252
+ raise ValueError(
253
+ "Toaster component must be created before sending a toast. (use `rx.toast.provider()`)"
254
+ )
245
255
  toast_command = f"{toast_ref}.{level}" if level is not None else toast_ref
256
+ if message == "" and ("title" not in props or "description" not in props):
257
+ raise ValueError("Toast message or title or description must be provided.")
246
258
  if props:
247
259
  args = serialize(ToastProps(**props)) # type: ignore
248
260
  toast = f"{toast_command}(`{message}`, {args})"
@@ -331,6 +343,20 @@ class Toaster(Component):
331
343
  )
332
344
  return call_script(dismiss_action)
333
345
 
346
+ @classmethod
347
+ def create(cls, *children, **props) -> Component:
348
+ """Create a toaster component.
349
+
350
+ Args:
351
+ *children: The children of the toaster.
352
+ **props: The properties of the toaster.
353
+
354
+ Returns:
355
+ The toaster component.
356
+ """
357
+ cls.is_used = True
358
+ return super().create(*children, **props)
359
+
334
360
 
335
361
  # TODO: figure out why loading toast stay open forever
336
362
  # def toast_loading(message: str, **kwargs):
@@ -3,7 +3,7 @@
3
3
  # ------------------- DO NOT EDIT ----------------------
4
4
  # This file was generated by `reflex/utils/pyi_generator.py`!
5
5
  # ------------------------------------------------------
6
- from typing import Any, Callable, Dict, Literal, Optional, Union, overload
6
+ from typing import Any, Callable, ClassVar, Dict, Literal, Optional, Union, overload
7
7
 
8
8
  from reflex.base import Base
9
9
  from reflex.components.component import Component, ComponentNamespace
@@ -52,9 +52,13 @@ class ToastProps(PropsBase):
52
52
  def dict(self, *args, **kwargs) -> dict[str, Any]: ...
53
53
 
54
54
  class Toaster(Component):
55
+ is_used: ClassVar[bool] = False
56
+
55
57
  def add_hooks(self) -> list[Var | str]: ...
56
58
  @staticmethod
57
- def send_toast(message: str, level: str | None = None, **props) -> EventSpec: ...
59
+ def send_toast(
60
+ message: str = "", level: str | None = None, **props
61
+ ) -> EventSpec: ...
58
62
  @staticmethod
59
63
  def toast_info(message: str, **kwargs): ...
60
64
  @staticmethod
@@ -158,10 +162,10 @@ class Toaster(Component):
158
162
  ] = None,
159
163
  **props,
160
164
  ) -> "Toaster":
161
- """Create the component.
165
+ """Create a toaster component.
162
166
 
163
167
  Args:
164
- *children: The children of the component.
168
+ *children: The children of the toaster.
165
169
  theme: the theme of the toast
166
170
  rich_colors: whether to show rich colors
167
171
  expand: whether to expand the toast
@@ -182,10 +186,10 @@ class Toaster(Component):
182
186
  class_name: The class name for the component.
183
187
  autofocus: Whether the component should take the focus once the page is loaded
184
188
  custom_attrs: custom attribute
185
- **props: The props of the component.
189
+ **props: The properties of the toaster.
186
190
 
187
191
  Returns:
188
- The component.
192
+ The toaster component.
189
193
  """
190
194
  ...
191
195
 
@@ -200,7 +204,7 @@ class ToastNamespace(ComponentNamespace):
200
204
 
201
205
  @staticmethod
202
206
  def __call__(
203
- message: str, level: Optional[str] = None, **props
207
+ message: str = "", level: Optional[str] = None, **props
204
208
  ) -> "Optional[EventSpec]":
205
209
  """Send a toast message.
206
210
 
@@ -209,6 +213,9 @@ class ToastNamespace(ComponentNamespace):
209
213
  level: The level of the toast.
210
214
  **props: The options for the toast.
211
215
 
216
+ Raises:
217
+ ValueError: If the Toaster component is not created.
218
+
212
219
  Returns:
213
220
  The toast event.
214
221
  """
reflex/constants/base.py CHANGED
@@ -94,6 +94,27 @@ class Templates(SimpleNamespace):
94
94
  # The default template
95
95
  DEFAULT = "blank"
96
96
 
97
+ # The reflex.build frontend host
98
+ REFLEX_BUILD_FRONTEND = os.environ.get(
99
+ "REFLEX_BUILD_FRONTEND", "https://flexgen.reflex.run"
100
+ )
101
+
102
+ # The reflex.build backend host
103
+ REFLEX_BUILD_BACKEND = os.environ.get(
104
+ "REFLEX_BUILD_BACKEND", "https://rxh-prod-flexgen.fly.dev"
105
+ )
106
+
107
+ # The URL to redirect to reflex.build
108
+ REFLEX_BUILD_URL = (
109
+ REFLEX_BUILD_FRONTEND + "/gen?reflex_init_token={reflex_init_token}"
110
+ )
111
+
112
+ # The URL to poll waiting for the user to select a generation.
113
+ REFLEX_BUILD_POLL_URL = REFLEX_BUILD_BACKEND + "/api/init/{reflex_init_token}"
114
+
115
+ # The URL to fetch the generation's reflex code
116
+ REFLEX_BUILD_CODE_URL = REFLEX_BUILD_BACKEND + "/api/gen/{generation_hash}"
117
+
97
118
  class Dirs(SimpleNamespace):
98
119
  """Folders used by the template system of Reflex."""
99
120
 
reflex/constants/event.py CHANGED
@@ -95,3 +95,5 @@ class EventTriggers(SimpleNamespace):
95
95
  ON_CLEAR_SERVER_ERRORS = "on_clear_server_errors"
96
96
  ON_VALUE_COMMIT = "on_value_commit"
97
97
  ON_SELECT = "on_select"
98
+ ON_ANIMATION_START = "on_animation_start"
99
+ ON_ANIMATION_END = "on_animation_end"
@@ -1,3 +1,20 @@
1
1
  """Experimental Immutable-Based Var System."""
2
2
 
3
3
  from .base import ImmutableVar as ImmutableVar
4
+ from .base import LiteralObjectVar as LiteralObjectVar
5
+ from .base import LiteralVar as LiteralVar
6
+ from .base import ObjectVar as ObjectVar
7
+ from .base import var_operation as var_operation
8
+ from .function import FunctionStringVar as FunctionStringVar
9
+ from .function import FunctionVar as FunctionVar
10
+ from .function import VarOperationCall as VarOperationCall
11
+ from .number import BooleanVar as BooleanVar
12
+ from .number import LiteralBooleanVar as LiteralBooleanVar
13
+ from .number import LiteralNumberVar as LiteralNumberVar
14
+ from .number import NumberVar as NumberVar
15
+ from .sequence import ArrayJoinOperation as ArrayJoinOperation
16
+ from .sequence import ArrayVar as ArrayVar
17
+ from .sequence import ConcatVarOperation as ConcatVarOperation
18
+ from .sequence import LiteralArrayVar as LiteralArrayVar
19
+ from .sequence import LiteralStringVar as LiteralStringVar
20
+ from .sequence import StringVar as StringVar
@@ -3,13 +3,32 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import dataclasses
6
+ import functools
6
7
  import sys
7
- from typing import Any, Optional, Type
8
+ from typing import (
9
+ Any,
10
+ Callable,
11
+ Dict,
12
+ Optional,
13
+ Type,
14
+ TypeVar,
15
+ Union,
16
+ )
17
+
18
+ from typing_extensions import ParamSpec
8
19
 
9
- from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
20
+ from reflex import constants
21
+ from reflex.base import Base
10
22
  from reflex.utils import serializers, types
11
23
  from reflex.utils.exceptions import VarTypeError
12
- from reflex.vars import Var, VarData, _decode_var, _extract_var_data, _global_vars
24
+ from reflex.vars import (
25
+ ImmutableVarData,
26
+ Var,
27
+ VarData,
28
+ _decode_var_immutable,
29
+ _extract_var_data,
30
+ _global_vars,
31
+ )
13
32
 
14
33
 
15
34
  @dataclasses.dataclass(
@@ -27,7 +46,15 @@ class ImmutableVar(Var):
27
46
  _var_type: Type = dataclasses.field(default=Any)
28
47
 
29
48
  # Extra metadata associated with the Var
30
- _var_data: Optional[VarData] = dataclasses.field(default=None)
49
+ _var_data: Optional[ImmutableVarData] = dataclasses.field(default=None)
50
+
51
+ def __str__(self) -> str:
52
+ """String representation of the var. Guaranteed to be a valid Javascript expression.
53
+
54
+ Returns:
55
+ The name of the var.
56
+ """
57
+ return self._var_name
31
58
 
32
59
  @property
33
60
  def _var_is_local(self) -> bool:
@@ -59,12 +86,31 @@ class ImmutableVar(Var):
59
86
  def __post_init__(self):
60
87
  """Post-initialize the var."""
61
88
  # Decode any inline Var markup and apply it to the instance
62
- _var_data, _var_name = _decode_var(self._var_name)
63
- if _var_data:
89
+ _var_data, _var_name = _decode_var_immutable(self._var_name)
90
+
91
+ if _var_data or _var_name != self._var_name:
64
92
  self.__init__(
65
- _var_name, self._var_type, VarData.merge(self._var_data, _var_data)
93
+ _var_name=_var_name,
94
+ _var_type=self._var_type,
95
+ _var_data=ImmutableVarData.merge(self._var_data, _var_data),
66
96
  )
67
97
 
98
+ def __hash__(self) -> int:
99
+ """Define a hash function for the var.
100
+
101
+ Returns:
102
+ The hash of the var.
103
+ """
104
+ return hash((self._var_name, self._var_type, self._var_data))
105
+
106
+ def _get_all_var_data(self) -> ImmutableVarData | None:
107
+ """Get all VarData associated with the Var.
108
+
109
+ Returns:
110
+ The VarData of the components and all of its children.
111
+ """
112
+ return self._var_data
113
+
68
114
  def _replace(self, merge_var_data=None, **kwargs: Any):
69
115
  """Make a copy of this Var with updated fields.
70
116
 
@@ -96,11 +142,11 @@ class ImmutableVar(Var):
96
142
  field_values = dict(
97
143
  _var_name=kwargs.pop("_var_name", self._var_name),
98
144
  _var_type=kwargs.pop("_var_type", self._var_type),
99
- _var_data=VarData.merge(
145
+ _var_data=ImmutableVarData.merge(
100
146
  kwargs.get("_var_data", self._var_data), merge_var_data
101
147
  ),
102
148
  )
103
- return ImmutableVar(**field_values)
149
+ return type(self)(**field_values)
104
150
 
105
151
  @classmethod
106
152
  def create(
@@ -109,7 +155,7 @@ class ImmutableVar(Var):
109
155
  _var_is_local: bool | None = None,
110
156
  _var_is_string: bool | None = None,
111
157
  _var_data: VarData | None = None,
112
- ) -> Var | None:
158
+ ) -> ImmutableVar | Var | None:
113
159
  """Create a var from a value.
114
160
 
115
161
  Args:
@@ -161,10 +207,18 @@ class ImmutableVar(Var):
161
207
  )
162
208
  name = name if isinstance(name, str) else format.json_dumps(name)
163
209
 
164
- return ImmutableVar(
210
+ return cls(
165
211
  _var_name=name,
166
212
  _var_type=type_,
167
- _var_data=_var_data,
213
+ _var_data=(
214
+ ImmutableVarData(
215
+ state=_var_data.state,
216
+ imports=_var_data.imports,
217
+ hooks=_var_data.hooks,
218
+ )
219
+ if _var_data
220
+ else None
221
+ ),
168
222
  )
169
223
 
170
224
  @classmethod
@@ -174,7 +228,7 @@ class ImmutableVar(Var):
174
228
  _var_is_local: bool | None = None,
175
229
  _var_is_string: bool | None = None,
176
230
  _var_data: VarData | None = None,
177
- ) -> Var:
231
+ ) -> Var | ImmutableVar:
178
232
  """Create a var from a value, asserting that it is not None.
179
233
 
180
234
  Args:
@@ -204,7 +258,220 @@ class ImmutableVar(Var):
204
258
  Returns:
205
259
  The formatted var.
206
260
  """
207
- _global_vars[hash(self)] = self
261
+ hashed_var = hash(self)
262
+
263
+ _global_vars[hashed_var] = self
208
264
 
209
265
  # Encode the _var_data into the formatted output for tracking purposes.
210
- return f"{REFLEX_VAR_OPENING_TAG}{hash(self)}{REFLEX_VAR_CLOSING_TAG}{self._var_name}"
266
+ return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._var_name}"
267
+
268
+
269
+ class ObjectVar(ImmutableVar):
270
+ """Base class for immutable object vars."""
271
+
272
+
273
+ class LiteralVar(ImmutableVar):
274
+ """Base class for immutable literal vars."""
275
+
276
+ @classmethod
277
+ def create(
278
+ cls,
279
+ value: Any,
280
+ _var_data: VarData | None = None,
281
+ ) -> Var:
282
+ """Create a var from a value.
283
+
284
+ Args:
285
+ value: The value to create the var from.
286
+ _var_data: Additional hooks and imports associated with the Var.
287
+
288
+ Returns:
289
+ The var.
290
+
291
+ Raises:
292
+ TypeError: If the value is not a supported type for LiteralVar.
293
+ """
294
+ if isinstance(value, Var):
295
+ if _var_data is None:
296
+ return value
297
+ return value._replace(merge_var_data=_var_data)
298
+
299
+ if value is None:
300
+ return ImmutableVar.create_safe("null", _var_data=_var_data)
301
+
302
+ if isinstance(value, Base):
303
+ return LiteralObjectVar(
304
+ value.dict(), _var_type=type(value), _var_data=_var_data
305
+ )
306
+
307
+ from .number import LiteralBooleanVar, LiteralNumberVar
308
+ from .sequence import LiteralArrayVar, LiteralStringVar
309
+
310
+ if isinstance(value, str):
311
+ return LiteralStringVar.create(value, _var_data=_var_data)
312
+
313
+ type_mapping = {
314
+ int: LiteralNumberVar,
315
+ float: LiteralNumberVar,
316
+ bool: LiteralBooleanVar,
317
+ dict: LiteralObjectVar,
318
+ list: LiteralArrayVar,
319
+ tuple: LiteralArrayVar,
320
+ set: LiteralArrayVar,
321
+ }
322
+
323
+ constructor = type_mapping.get(type(value))
324
+
325
+ if constructor is None:
326
+ raise TypeError(f"Unsupported type {type(value)} for LiteralVar.")
327
+
328
+ return constructor(value, _var_data=_var_data)
329
+
330
+ def __post_init__(self):
331
+ """Post-initialize the var."""
332
+
333
+
334
+ @dataclasses.dataclass(
335
+ eq=False,
336
+ frozen=True,
337
+ **{"slots": True} if sys.version_info >= (3, 10) else {},
338
+ )
339
+ class LiteralObjectVar(LiteralVar):
340
+ """Base class for immutable literal object vars."""
341
+
342
+ _var_value: Dict[Union[Var, Any], Union[Var, Any]] = dataclasses.field(
343
+ default_factory=dict
344
+ )
345
+
346
+ def __init__(
347
+ self,
348
+ _var_value: dict[Var | Any, Var | Any],
349
+ _var_type: Type = dict,
350
+ _var_data: VarData | None = None,
351
+ ):
352
+ """Initialize the object var.
353
+
354
+ Args:
355
+ _var_value: The value of the var.
356
+ _var_data: Additional hooks and imports associated with the Var.
357
+ """
358
+ super(LiteralObjectVar, self).__init__(
359
+ _var_name="",
360
+ _var_type=_var_type,
361
+ _var_data=ImmutableVarData.merge(_var_data),
362
+ )
363
+ object.__setattr__(
364
+ self,
365
+ "_var_value",
366
+ _var_value,
367
+ )
368
+ object.__delattr__(self, "_var_name")
369
+
370
+ def __getattr__(self, name):
371
+ """Get an attribute of the var.
372
+
373
+ Args:
374
+ name: The name of the attribute.
375
+
376
+ Returns:
377
+ The attribute of the var.
378
+ """
379
+ if name == "_var_name":
380
+ return self._cached_var_name
381
+ return super(type(self), self).__getattr__(name)
382
+
383
+ @functools.cached_property
384
+ def _cached_var_name(self) -> str:
385
+ """The name of the var.
386
+
387
+ Returns:
388
+ The name of the var.
389
+ """
390
+ return (
391
+ "{ "
392
+ + ", ".join(
393
+ [
394
+ f"[{str(LiteralVar.create(key))}] : {str(LiteralVar.create(value))}"
395
+ for key, value in self._var_value.items()
396
+ ]
397
+ )
398
+ + " }"
399
+ )
400
+
401
+ @functools.cached_property
402
+ def _cached_get_all_var_data(self) -> ImmutableVarData | None:
403
+ """Get all VarData associated with the Var.
404
+
405
+ Returns:
406
+ The VarData of the components and all of its children.
407
+ """
408
+ return ImmutableVarData.merge(
409
+ *[
410
+ value._get_all_var_data()
411
+ for key, value in self._var_value
412
+ if isinstance(value, Var)
413
+ ],
414
+ *[
415
+ key._get_all_var_data()
416
+ for key, value in self._var_value
417
+ if isinstance(key, Var)
418
+ ],
419
+ self._var_data,
420
+ )
421
+
422
+ def _get_all_var_data(self) -> ImmutableVarData | None:
423
+ """Wrapper method for cached property.
424
+
425
+ Returns:
426
+ The VarData of the components and all of its children.
427
+ """
428
+ return self._cached_get_all_var_data
429
+
430
+
431
+ P = ParamSpec("P")
432
+ T = TypeVar("T", bound=ImmutableVar)
433
+
434
+
435
+ def var_operation(*, output: Type[T]) -> Callable[[Callable[P, str]], Callable[P, T]]:
436
+ """Decorator for creating a var operation.
437
+
438
+ Example:
439
+ ```python
440
+ @var_operation(output=NumberVar)
441
+ def add(a: NumberVar, b: NumberVar):
442
+ return f"({a} + {b})"
443
+ ```
444
+
445
+ Args:
446
+ output: The output type of the operation.
447
+
448
+ Returns:
449
+ The decorator.
450
+ """
451
+
452
+ def decorator(func: Callable[P, str], output=output):
453
+ @functools.wraps(func)
454
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
455
+ args_vars = [
456
+ LiteralVar.create(arg) if not isinstance(arg, Var) else arg
457
+ for arg in args
458
+ ]
459
+ kwargs_vars = {
460
+ key: LiteralVar.create(value) if not isinstance(value, Var) else value
461
+ for key, value in kwargs.items()
462
+ }
463
+ return output(
464
+ _var_name=func(*args_vars, **kwargs_vars), # type: ignore
465
+ _var_data=VarData.merge(
466
+ *[arg._get_all_var_data() for arg in args if isinstance(arg, Var)],
467
+ *[
468
+ arg._get_all_var_data()
469
+ for arg in kwargs.values()
470
+ if isinstance(arg, Var)
471
+ ],
472
+ ),
473
+ )
474
+
475
+ return wrapper
476
+
477
+ return decorator