reflex 0.5.0.post1__py3-none-any.whl → 0.5.1a1__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 (41) hide show
  1. reflex/.templates/web/utils/state.js +7 -2
  2. reflex/app.py +69 -50
  3. reflex/base.py +5 -2
  4. reflex/components/component.py +49 -13
  5. reflex/components/core/__init__.py +7 -1
  6. reflex/components/core/banner.py +79 -6
  7. reflex/components/core/banner.pyi +130 -0
  8. reflex/components/core/cond.py +1 -1
  9. reflex/components/core/debounce.py +2 -4
  10. reflex/components/core/foreach.py +11 -0
  11. reflex/components/core/upload.py +9 -10
  12. reflex/components/el/elements/forms.py +12 -6
  13. reflex/components/el/elements/media.py +19 -0
  14. reflex/components/el/elements/media.pyi +3 -1
  15. reflex/components/gridjs/datatable.py +4 -2
  16. reflex/components/props.py +30 -0
  17. reflex/components/radix/themes/components/tabs.py +1 -1
  18. reflex/components/sonner/toast.py +102 -35
  19. reflex/components/sonner/toast.pyi +27 -14
  20. reflex/config.py +5 -3
  21. reflex/constants/compiler.py +3 -3
  22. reflex/constants/installer.py +1 -1
  23. reflex/event.py +38 -24
  24. reflex/experimental/__init__.py +4 -0
  25. reflex/experimental/client_state.py +198 -0
  26. reflex/state.py +61 -21
  27. reflex/style.py +3 -3
  28. reflex/testing.py +28 -9
  29. reflex/utils/exceptions.py +64 -8
  30. reflex/utils/format.py +73 -2
  31. reflex/utils/prerequisites.py +28 -18
  32. reflex/utils/processes.py +34 -4
  33. reflex/utils/telemetry.py +5 -5
  34. reflex/utils/types.py +16 -0
  35. reflex/vars.py +104 -61
  36. reflex/vars.pyi +7 -6
  37. {reflex-0.5.0.post1.dist-info → reflex-0.5.1a1.dist-info}/METADATA +1 -1
  38. {reflex-0.5.0.post1.dist-info → reflex-0.5.1a1.dist-info}/RECORD +41 -39
  39. {reflex-0.5.0.post1.dist-info → reflex-0.5.1a1.dist-info}/LICENSE +0 -0
  40. {reflex-0.5.0.post1.dist-info → reflex-0.5.1a1.dist-info}/WHEEL +0 -0
  41. {reflex-0.5.0.post1.dist-info → reflex-0.5.1a1.dist-info}/entry_points.txt +0 -0
@@ -20,11 +20,15 @@ from reflex.components.radix.themes.components.dialog import (
20
20
  )
21
21
  from reflex.components.radix.themes.layout import Flex
22
22
  from reflex.components.radix.themes.typography.text import Text
23
+ from reflex.components.sonner.toast import Toaster, ToastProps
23
24
  from reflex.constants import Dirs, Hooks, Imports
25
+ from reflex.constants.compiler import CompileVars
24
26
  from reflex.utils import imports
27
+ from reflex.utils.serializers import serialize
25
28
  from reflex.vars import Var, VarData
26
29
 
27
30
  connect_error_var_data: VarData
31
+ connect_errors: Var
28
32
  connection_error: Var
29
33
  connection_errors_count: Var
30
34
  has_connection_errors: Var
@@ -99,6 +103,132 @@ class WebsocketTargetURL(Bare):
99
103
 
100
104
  def default_connection_error() -> list[str | Var | Component]: ...
101
105
 
106
+ class ConnectionToaster(Toaster):
107
+ def add_hooks(self) -> list[str]: ...
108
+ @overload
109
+ @classmethod
110
+ def create( # type: ignore
111
+ cls,
112
+ *children,
113
+ theme: Optional[Union[Var[str], str]] = None,
114
+ rich_colors: Optional[Union[Var[bool], bool]] = None,
115
+ expand: Optional[Union[Var[bool], bool]] = None,
116
+ visible_toasts: Optional[Union[Var[int], int]] = None,
117
+ position: Optional[
118
+ Union[
119
+ Var[
120
+ Literal[
121
+ "top-left",
122
+ "top-center",
123
+ "top-right",
124
+ "bottom-left",
125
+ "bottom-center",
126
+ "bottom-right",
127
+ ]
128
+ ],
129
+ Literal[
130
+ "top-left",
131
+ "top-center",
132
+ "top-right",
133
+ "bottom-left",
134
+ "bottom-center",
135
+ "bottom-right",
136
+ ],
137
+ ]
138
+ ] = None,
139
+ close_button: Optional[Union[Var[bool], bool]] = None,
140
+ offset: Optional[Union[Var[str], str]] = None,
141
+ dir: Optional[Union[Var[str], str]] = None,
142
+ hotkey: Optional[Union[Var[str], str]] = None,
143
+ invert: Optional[Union[Var[bool], bool]] = None,
144
+ toast_options: Optional[Union[Var[ToastProps], ToastProps]] = None,
145
+ gap: Optional[Union[Var[int], int]] = None,
146
+ loading_icon: Optional[Union[Var[Icon], Icon]] = None,
147
+ pause_when_page_is_hidden: Optional[Union[Var[bool], bool]] = None,
148
+ style: Optional[Style] = None,
149
+ key: Optional[Any] = None,
150
+ id: Optional[Any] = None,
151
+ class_name: Optional[Any] = None,
152
+ autofocus: Optional[bool] = None,
153
+ custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
154
+ on_blur: Optional[
155
+ Union[EventHandler, EventSpec, list, function, BaseVar]
156
+ ] = None,
157
+ on_click: Optional[
158
+ Union[EventHandler, EventSpec, list, function, BaseVar]
159
+ ] = None,
160
+ on_context_menu: Optional[
161
+ Union[EventHandler, EventSpec, list, function, BaseVar]
162
+ ] = None,
163
+ on_double_click: Optional[
164
+ Union[EventHandler, EventSpec, list, function, BaseVar]
165
+ ] = None,
166
+ on_focus: Optional[
167
+ Union[EventHandler, EventSpec, list, function, BaseVar]
168
+ ] = None,
169
+ on_mount: Optional[
170
+ Union[EventHandler, EventSpec, list, function, BaseVar]
171
+ ] = None,
172
+ on_mouse_down: Optional[
173
+ Union[EventHandler, EventSpec, list, function, BaseVar]
174
+ ] = None,
175
+ on_mouse_enter: Optional[
176
+ Union[EventHandler, EventSpec, list, function, BaseVar]
177
+ ] = None,
178
+ on_mouse_leave: Optional[
179
+ Union[EventHandler, EventSpec, list, function, BaseVar]
180
+ ] = None,
181
+ on_mouse_move: Optional[
182
+ Union[EventHandler, EventSpec, list, function, BaseVar]
183
+ ] = None,
184
+ on_mouse_out: Optional[
185
+ Union[EventHandler, EventSpec, list, function, BaseVar]
186
+ ] = None,
187
+ on_mouse_over: Optional[
188
+ Union[EventHandler, EventSpec, list, function, BaseVar]
189
+ ] = None,
190
+ on_mouse_up: Optional[
191
+ Union[EventHandler, EventSpec, list, function, BaseVar]
192
+ ] = None,
193
+ on_scroll: Optional[
194
+ Union[EventHandler, EventSpec, list, function, BaseVar]
195
+ ] = None,
196
+ on_unmount: Optional[
197
+ Union[EventHandler, EventSpec, list, function, BaseVar]
198
+ ] = None,
199
+ **props
200
+ ) -> "ConnectionToaster":
201
+ """Create the component.
202
+
203
+ Args:
204
+ *children: The children of the component.
205
+ theme: the theme of the toast
206
+ rich_colors: whether to show rich colors
207
+ expand: whether to expand the toast
208
+ visible_toasts: the number of toasts that are currently visible
209
+ position: the position of the toast
210
+ close_button: whether to show the close button
211
+ offset: offset of the toast
212
+ dir: directionality of the toast (default: ltr)
213
+ hotkey: Keyboard shortcut that will move focus to the toaster area.
214
+ invert: Dark toasts in light mode and vice versa.
215
+ toast_options: These will act as default options for all toasts. See toast() for all available options.
216
+ gap: Gap between toasts when expanded
217
+ loading_icon: Changes the default loading icon
218
+ pause_when_page_is_hidden: Pauses toast timers when the page is hidden, e.g., when the tab is backgrounded, the browser is minimized, or the OS is locked.
219
+ style: The style of the component.
220
+ key: A unique key for the component.
221
+ id: The id for the component.
222
+ class_name: The class name for the component.
223
+ autofocus: Whether the component should take the focus once the page is loaded
224
+ custom_attrs: custom attribute
225
+ **props: The props of the component.
226
+
227
+ Returns:
228
+ The component.
229
+ """
230
+ ...
231
+
102
232
  class ConnectionBanner(Component):
103
233
  @overload
104
234
  @classmethod
@@ -13,7 +13,7 @@ from reflex.utils import format, imports
13
13
  from reflex.vars import BaseVar, Var, VarData
14
14
 
15
15
  _IS_TRUE_IMPORT = {
16
- f"/{Dirs.STATE_PATH}": {imports.ImportVar(tag="isTrue")},
16
+ f"/{Dirs.STATE_PATH}": [imports.ImportVar(tag="isTrue")],
17
17
  }
18
18
 
19
19
 
@@ -109,13 +109,11 @@ class DebounceInput(Component):
109
109
  "{%s}" % (child.alias or child.tag),
110
110
  _var_is_local=False,
111
111
  _var_is_string=False,
112
- )._replace(
113
- _var_type=Type[Component],
114
- merge_var_data=VarData( # type: ignore
112
+ _var_data=VarData(
115
113
  imports=child._get_imports(),
116
114
  hooks=child._get_hooks_internal(),
117
115
  ),
118
- ),
116
+ ).to(Type[Component]),
119
117
  )
120
118
 
121
119
  component = super().create(**props)
@@ -8,6 +8,7 @@ from reflex.components.base.fragment import Fragment
8
8
  from reflex.components.component import Component
9
9
  from reflex.components.tags import IterTag
10
10
  from reflex.constants import MemoizationMode
11
+ from reflex.state import ComponentState
11
12
  from reflex.utils import console
12
13
  from reflex.vars import Var
13
14
 
@@ -50,6 +51,7 @@ class Foreach(Component):
50
51
 
51
52
  Raises:
52
53
  ForeachVarError: If the iterable is of type Any.
54
+ TypeError: If the render function is a ComponentState.
53
55
  """
54
56
  if props:
55
57
  console.deprecate(
@@ -65,6 +67,15 @@ class Foreach(Component):
65
67
  "(If you are trying to foreach over a state var, add a type annotation to the var). "
66
68
  "See https://reflex.dev/docs/library/layout/foreach/"
67
69
  )
70
+
71
+ if (
72
+ hasattr(render_fn, "__qualname__")
73
+ and render_fn.__qualname__ == ComponentState.create.__qualname__
74
+ ):
75
+ raise TypeError(
76
+ "Using a ComponentState as `render_fn` inside `rx.foreach` is not supported yet."
77
+ )
78
+
68
79
  component = cls(
69
80
  iterable=iterable,
70
81
  render_fn=render_fn,
@@ -24,12 +24,12 @@ from reflex.vars import BaseVar, CallableVar, Var, VarData
24
24
 
25
25
  DEFAULT_UPLOAD_ID: str = "default"
26
26
 
27
- upload_files_context_var_data: VarData = VarData( # type: ignore
27
+ upload_files_context_var_data: VarData = VarData(
28
28
  imports={
29
- "react": {imports.ImportVar(tag="useContext")},
30
- f"/{Dirs.CONTEXTS_PATH}": {
29
+ "react": [imports.ImportVar(tag="useContext")],
30
+ f"/{Dirs.CONTEXTS_PATH}": [
31
31
  imports.ImportVar(tag="UploadFilesContext"),
32
- },
32
+ ],
33
33
  },
34
34
  hooks={
35
35
  "const [filesById, setFilesById] = useContext(UploadFilesContext);": None,
@@ -118,14 +118,13 @@ def get_upload_dir() -> Path:
118
118
 
119
119
 
120
120
  uploaded_files_url_prefix: Var = Var.create_safe(
121
- "${getBackendURL(env.UPLOAD)}"
122
- )._replace(
123
- merge_var_data=VarData( # type: ignore
121
+ "${getBackendURL(env.UPLOAD)}",
122
+ _var_data=VarData(
124
123
  imports={
125
- f"/{Dirs.STATE_PATH}": {imports.ImportVar(tag="getBackendURL")},
126
- "/env.json": {imports.ImportVar(tag="env", is_default=True)},
124
+ f"/{Dirs.STATE_PATH}": [imports.ImportVar(tag="getBackendURL")],
125
+ "/env.json": [imports.ImportVar(tag="env", is_default=True)],
127
126
  }
128
- )
127
+ ),
129
128
  )
130
129
 
131
130
 
@@ -216,13 +216,17 @@ class Form(BaseHTML):
216
216
  if ref.startswith("refs_"):
217
217
  ref_var = Var.create_safe(ref[:-3]).as_ref()
218
218
  form_refs[ref[5:-3]] = Var.create_safe(
219
- f"getRefValues({str(ref_var)})", _var_is_local=False
220
- )._replace(merge_var_data=ref_var._var_data)
219
+ f"getRefValues({str(ref_var)})",
220
+ _var_is_local=False,
221
+ _var_data=ref_var._var_data,
222
+ )
221
223
  else:
222
224
  ref_var = Var.create_safe(ref).as_ref()
223
225
  form_refs[ref[4:]] = Var.create_safe(
224
- f"getRefValue({str(ref_var)})", _var_is_local=False
225
- )._replace(merge_var_data=ref_var._var_data)
226
+ f"getRefValue({str(ref_var)})",
227
+ _var_is_local=False,
228
+ _var_data=ref_var._var_data,
229
+ )
226
230
  return form_refs
227
231
 
228
232
  def _get_vars(self, include_children: bool = True) -> Iterator[Var]:
@@ -619,14 +623,16 @@ class Textarea(BaseHTML):
619
623
  on_key_down=Var.create_safe(
620
624
  f"(e) => enterKeySubmitOnKeyDown(e, {self.enter_key_submit._var_name_unwrapped})",
621
625
  _var_is_local=False,
622
- )._replace(merge_var_data=self.enter_key_submit._var_data),
626
+ _var_data=self.enter_key_submit._var_data,
627
+ )
623
628
  )
624
629
  if self.auto_height is not None:
625
630
  tag.add_props(
626
631
  on_input=Var.create_safe(
627
632
  f"(e) => autoHeightOnInput(e, {self.auto_height._var_name_unwrapped})",
628
633
  _var_is_local=False,
629
- )._replace(merge_var_data=self.auto_height._var_data),
634
+ _var_data=self.auto_height._var_data,
635
+ )
630
636
  )
631
637
  return tag
632
638
 
@@ -1,6 +1,7 @@
1
1
  """Element classes. This is an auto-generated file. Do not edit. See ../generate.py."""
2
2
  from typing import Any, Union
3
3
 
4
+ from reflex import Component
4
5
  from reflex.vars import Var as Var
5
6
 
6
7
  from .base import BaseHTML
@@ -116,6 +117,24 @@ class Img(BaseHTML):
116
117
  # The name of the map to use with the image
117
118
  use_map: Var[Union[str, int, bool]]
118
119
 
120
+ @classmethod
121
+ def create(cls, *children, **props) -> Component:
122
+ """Override create method to apply source attribute to value if user fails to pass in attribute.
123
+
124
+ Args:
125
+ *children: The children of the component.
126
+ **props: The props of the component.
127
+
128
+ Returns:
129
+ The component.
130
+
131
+ """
132
+ return (
133
+ super().create(src=children[0], **props)
134
+ if children
135
+ else super().create(*children, **props)
136
+ )
137
+
119
138
 
120
139
  class Map(BaseHTML):
121
140
  """Display the map element."""
@@ -8,6 +8,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
10
  from typing import Any, Union
11
+ from reflex import Component
11
12
  from reflex.vars import Var as Var
12
13
  from .base import BaseHTML
13
14
 
@@ -470,7 +471,7 @@ class Img(BaseHTML):
470
471
  ] = None,
471
472
  **props
472
473
  ) -> "Img":
473
- """Create the component.
474
+ """Override create method to apply source attribute to value if user fails to pass in attribute.
474
475
 
475
476
  Args:
476
477
  *children: The children of the component.
@@ -512,6 +513,7 @@ class Img(BaseHTML):
512
513
 
513
514
  Returns:
514
515
  The component.
516
+
515
517
  """
516
518
  ...
517
519
 
@@ -114,12 +114,14 @@ class DataTable(Gridjs):
114
114
  _var_name=f"{self.data._var_name}.columns",
115
115
  _var_type=List[Any],
116
116
  _var_full_name_needs_state_prefix=True,
117
- )._replace(merge_var_data=self.data._var_data)
117
+ _var_data=self.data._var_data,
118
+ )
118
119
  self.data = BaseVar(
119
120
  _var_name=f"{self.data._var_name}.data",
120
121
  _var_type=List[List[Any]],
121
122
  _var_full_name_needs_state_prefix=True,
122
- )._replace(merge_var_data=self.data._var_data)
123
+ _var_data=self.data._var_data,
124
+ )
123
125
  if types.is_dataframe(type(self.data)):
124
126
  # If given a pandas df break up the data and columns
125
127
  data = serialize(self.data)
@@ -0,0 +1,30 @@
1
+ """A class that holds props to be passed or applied to a component."""
2
+ from __future__ import annotations
3
+
4
+ from reflex.base import Base
5
+ from reflex.utils import format
6
+ from reflex.utils.serializers import serialize
7
+
8
+
9
+ class PropsBase(Base):
10
+ """Base for a class containing props that can be serialized as a JS object."""
11
+
12
+ def json(self) -> str:
13
+ """Convert the object to a json-like string.
14
+
15
+ Vars will be unwrapped so they can represent actual JS var names and functions.
16
+
17
+ Keys will be converted to camelCase.
18
+
19
+ Returns:
20
+ The object as a Javascript Object literal.
21
+ """
22
+ return format.unwrap_vars(
23
+ self.__config__.json_dumps(
24
+ {
25
+ format.to_camel_case(key): value
26
+ for key, value in self.dict().items()
27
+ },
28
+ default=serialize,
29
+ )
30
+ )
@@ -68,7 +68,7 @@ class TabsTrigger(RadixThemesComponent):
68
68
  _valid_parents: List[str] = ["TabsList"]
69
69
 
70
70
  @classmethod
71
- def create(self, *children, **props) -> Component:
71
+ def create(cls, *children, **props) -> Component:
72
72
  """Create a TabsTrigger component.
73
73
 
74
74
  Args:
@@ -2,16 +2,20 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Literal
5
+ from typing import Any, Literal, Optional, Union
6
6
 
7
7
  from reflex.base import Base
8
8
  from reflex.components.component import Component, ComponentNamespace
9
9
  from reflex.components.lucide.icon import Icon
10
- from reflex.event import EventSpec, call_script
10
+ from reflex.components.props import PropsBase
11
+ from reflex.event import (
12
+ EventSpec,
13
+ call_script,
14
+ )
11
15
  from reflex.style import Style, color_mode
12
16
  from reflex.utils import format
13
17
  from reflex.utils.imports import ImportVar
14
- from reflex.utils.serializers import serialize
18
+ from reflex.utils.serializers import serialize, serializer
15
19
  from reflex.vars import Var, VarData
16
20
 
17
21
  LiteralPosition = Literal[
@@ -27,46 +31,68 @@ LiteralPosition = Literal[
27
31
  toast_ref = Var.create_safe("refs['__toast']")
28
32
 
29
33
 
30
- class PropsBase(Base):
31
- """Base class for all props classes."""
34
+ class ToastAction(Base):
35
+ """A toast action that render a button in the toast."""
32
36
 
33
- def json(self) -> str:
34
- """Convert the object to a json string.
37
+ label: str
38
+ on_click: Any
35
39
 
36
- Returns:
37
- The object as a json string.
38
- """
39
- from reflex.utils.serializers import serialize
40
40
 
41
- return self.__config__.json_dumps(
42
- {format.to_camel_case(key): value for key, value in self.dict().items()},
43
- default=serialize,
41
+ @serializer
42
+ def serialize_action(action: ToastAction) -> dict:
43
+ """Serialize a toast action.
44
+
45
+ Args:
46
+ action: The toast action to serialize.
47
+
48
+ Returns:
49
+ The serialized toast action with on_click formatted to queue the given event.
50
+ """
51
+ return {
52
+ "label": action.label,
53
+ "onClick": format.format_queue_events(action.on_click),
54
+ }
55
+
56
+
57
+ def _toast_callback_signature(toast: Var) -> list[Var]:
58
+ """The signature for the toast callback, stripping out unserializable keys.
59
+
60
+ Args:
61
+ toast: The toast variable.
62
+
63
+ Returns:
64
+ A function call stripping non-serializable members of the toast object.
65
+ """
66
+ return [
67
+ Var.create_safe(
68
+ f"(() => {{let {{action, cancel, onDismiss, onAutoClose, ...rest}} = {toast}; return rest}})()"
44
69
  )
70
+ ]
45
71
 
46
72
 
47
73
  class ToastProps(PropsBase):
48
74
  """Props for the toast component."""
49
75
 
50
76
  # Toast's description, renders underneath the title.
51
- description: str = ""
77
+ description: Optional[Union[str, Var]]
52
78
 
53
79
  # Whether to show the close button.
54
- close_button: bool = False
80
+ close_button: Optional[bool]
55
81
 
56
82
  # Dark toast in light mode and vice versa.
57
- invert: bool = False
83
+ invert: Optional[bool]
58
84
 
59
85
  # Control the sensitivity of the toast for screen readers
60
- important: bool = False
86
+ important: Optional[bool]
61
87
 
62
88
  # Time in milliseconds that should elapse before automatically closing the toast.
63
- duration: int = 4000
89
+ duration: Optional[int]
64
90
 
65
91
  # Position of the toast.
66
- position: LiteralPosition = "bottom-right"
92
+ position: Optional[LiteralPosition]
67
93
 
68
94
  # If false, it'll prevent the user from dismissing the toast.
69
- dismissible: bool = True
95
+ dismissible: Optional[bool]
70
96
 
71
97
  # TODO: fix serialization of icons for toast? (might not be possible yet)
72
98
  # Icon displayed in front of toast's text, aligned vertically.
@@ -74,31 +100,69 @@ class ToastProps(PropsBase):
74
100
 
75
101
  # TODO: fix implementation for action / cancel buttons
76
102
  # Renders a primary button, clicking it will close the toast.
77
- # action: str = ""
103
+ action: Optional[ToastAction]
78
104
 
79
105
  # Renders a secondary button, clicking it will close the toast.
80
- # cancel: str = ""
106
+ cancel: Optional[ToastAction]
81
107
 
82
108
  # Custom id for the toast.
83
- id: str = ""
109
+ id: Optional[str]
84
110
 
85
111
  # Removes the default styling, which allows for easier customization.
86
- unstyled: bool = False
112
+ unstyled: Optional[bool]
87
113
 
88
114
  # Custom style for the toast.
89
- style: Style = Style()
115
+ style: Optional[Style]
90
116
 
117
+ # XXX: These still do not seem to work
91
118
  # Custom style for the toast primary button.
92
- # action_button_styles: Style = Style()
119
+ action_button_styles: Optional[Style]
93
120
 
94
121
  # Custom style for the toast secondary button.
95
- # cancel_button_styles: Style = Style()
122
+ cancel_button_styles: Optional[Style]
123
+
124
+ # The function gets called when either the close button is clicked, or the toast is swiped.
125
+ on_dismiss: Optional[Any]
126
+
127
+ # Function that gets called when the toast disappears automatically after it's timeout (duration` prop).
128
+ on_auto_close: Optional[Any]
129
+
130
+ def dict(self, *args, **kwargs) -> dict:
131
+ """Convert the object to a dictionary.
132
+
133
+ Args:
134
+ *args: The arguments to pass to the base class.
135
+ **kwargs: The keyword arguments to pass to the base
136
+
137
+ Returns:
138
+ The object as a dictionary with ToastAction fields intact.
139
+ """
140
+ kwargs.setdefault("exclude_none", True)
141
+ d = super().dict(*args, **kwargs)
142
+ # Keep these fields as ToastAction so they can be serialized specially
143
+ if "action" in d:
144
+ d["action"] = self.action
145
+ if isinstance(self.action, dict):
146
+ d["action"] = ToastAction(**self.action)
147
+ if "cancel" in d:
148
+ d["cancel"] = self.cancel
149
+ if isinstance(self.cancel, dict):
150
+ d["cancel"] = ToastAction(**self.cancel)
151
+ if "on_dismiss" in d:
152
+ d["on_dismiss"] = format.format_queue_events(
153
+ self.on_dismiss, _toast_callback_signature
154
+ )
155
+ if "on_auto_close" in d:
156
+ d["on_auto_close"] = format.format_queue_events(
157
+ self.on_auto_close, _toast_callback_signature
158
+ )
159
+ return d
96
160
 
97
161
 
98
162
  class Toaster(Component):
99
163
  """A Toaster Component for displaying toast notifications."""
100
164
 
101
- library = "sonner@1.4.41"
165
+ library: str = "sonner@1.4.41"
102
166
 
103
167
  tag = "Toaster"
104
168
 
@@ -145,12 +209,15 @@ class Toaster(Component):
145
209
  pause_when_page_is_hidden: Var[bool]
146
210
 
147
211
  def _get_hooks(self) -> Var[str]:
148
- hook = Var.create_safe(f"{toast_ref} = toast", _var_is_local=True)
149
- hook._var_data = VarData( # type: ignore
150
- imports={
151
- "/utils/state": [ImportVar(tag="refs")],
152
- self.library: [ImportVar(tag="toast", install=False)],
153
- }
212
+ hook = Var.create_safe(
213
+ f"{toast_ref} = toast",
214
+ _var_is_local=True,
215
+ _var_data=VarData(
216
+ imports={
217
+ "/utils/state": [ImportVar(tag="refs")],
218
+ self.library: [ImportVar(tag="toast", install=False)],
219
+ }
220
+ ),
154
221
  )
155
222
  return hook
156
223
 
@@ -7,15 +7,16 @@ 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 Literal
10
+ from typing import Any, Literal, Optional, Union
11
11
  from reflex.base import Base
12
12
  from reflex.components.component import Component, ComponentNamespace
13
13
  from reflex.components.lucide.icon import Icon
14
+ from reflex.components.props import PropsBase
14
15
  from reflex.event import EventSpec, call_script
15
16
  from reflex.style import Style, color_mode
16
17
  from reflex.utils import format
17
18
  from reflex.utils.imports import ImportVar
18
- from reflex.utils.serializers import serialize
19
+ from reflex.utils.serializers import serialize, serializer
19
20
  from reflex.vars import Var, VarData
20
21
 
21
22
  LiteralPosition = Literal[
@@ -28,20 +29,32 @@ LiteralPosition = Literal[
28
29
  ]
29
30
  toast_ref = Var.create_safe("refs['__toast']")
30
31
 
31
- class PropsBase(Base):
32
- def json(self) -> str: ...
32
+ class ToastAction(Base):
33
+ label: str
34
+ on_click: Any
35
+
36
+ @serializer
37
+ def serialize_action(action: ToastAction) -> dict: ...
33
38
 
34
39
  class ToastProps(PropsBase):
35
- description: str
36
- close_button: bool
37
- invert: bool
38
- important: bool
39
- duration: int
40
- position: LiteralPosition
41
- dismissible: bool
42
- id: str
43
- unstyled: bool
44
- style: Style
40
+ description: Optional[Union[str, Var]]
41
+ close_button: Optional[bool]
42
+ invert: Optional[bool]
43
+ important: Optional[bool]
44
+ duration: Optional[int]
45
+ position: Optional[LiteralPosition]
46
+ dismissible: Optional[bool]
47
+ action: Optional[ToastAction]
48
+ cancel: Optional[ToastAction]
49
+ id: Optional[str]
50
+ unstyled: Optional[bool]
51
+ style: Optional[Style]
52
+ action_button_styles: Optional[Style]
53
+ cancel_button_styles: Optional[Style]
54
+ on_dismiss: Optional[Any]
55
+ on_auto_close: Optional[Any]
56
+
57
+ def dict(self, *args, **kwargs) -> dict: ...
45
58
 
46
59
  class Toaster(Component):
47
60
  @staticmethod