1
0
Эх сурвалжийг харах

promote toast to main namespace (#3422)

Thomas Brandého 11 сар өмнө
parent
commit
e1244b4ea6

+ 7 - 1
reflex/__init__.py

@@ -248,7 +248,12 @@ _MAPPING: dict = {
     "admin": ["AdminDash"],
     "app": ["App", "UploadFile"],
     "base": ["Base"],
-    "components.component": ["Component", "NoSSRComponent", "memo"],
+    "components.component": [
+        "Component",
+        "NoSSRComponent",
+        "memo",
+        "ComponentNamespace",
+    ],
     "components.el.elements.media": ["image"],
     "components.lucide": ["icon"],
     **COMPONENTS_BASE_MAPPING,
@@ -270,6 +275,7 @@ _MAPPING: dict = {
         "data_editor",
         "data_editor_theme",
     ],
+    "components.sonner.toast": ["toast"],
     "components.datadisplay.logo": ["logo"],
     "components.gridjs": ["data_table"],
     "components.moment": ["MomentDelta", "moment"],

+ 2 - 0
reflex/__init__.pyi

@@ -24,6 +24,7 @@ from .base import Base as Base
 from .components.component import Component as Component
 from .components.component import NoSSRComponent as NoSSRComponent
 from .components.component import memo as memo
+from .components.component import ComponentNamespace as ComponentNamespace
 from .components.el.elements.media import image as image
 from .components.lucide import icon as icon
 from .components.base.fragment import fragment as fragment
@@ -143,6 +144,7 @@ from .components.core.upload import upload as upload
 from .components.datadisplay.code import code_block as code_block
 from .components.datadisplay.dataeditor import data_editor as data_editor
 from .components.datadisplay.dataeditor import data_editor_theme as data_editor_theme
+from .components.sonner.toast import toast as toast
 from .components.datadisplay.logo import logo as logo
 from .components.gridjs import data_table as data_table
 from .components.moment import MomentDelta as MomentDelta

+ 30 - 14
reflex/components/sonner/toast.py

@@ -106,7 +106,7 @@ class ToastProps(PropsBase):
     cancel: Optional[ToastAction]
 
     # Custom id for the toast.
-    id: Optional[str]
+    id: Optional[Union[str, Var]]
 
     # Removes the default styling, which allows for easier customization.
     unstyled: Optional[bool]
@@ -127,7 +127,7 @@ class ToastProps(PropsBase):
     # Function that gets called when the toast disappears automatically after it's timeout (duration` prop).
     on_auto_close: Optional[Any]
 
-    def dict(self, *args, **kwargs) -> dict:
+    def dict(self, *args, **kwargs) -> dict[str, Any]:
         """Convert the object to a dictionary.
 
         Args:
@@ -137,7 +137,7 @@ class ToastProps(PropsBase):
         Returns:
             The object as a dictionary with ToastAction fields intact.
         """
-        kwargs.setdefault("exclude_none", True)
+        kwargs.setdefault("exclude_none", True)  # type: ignore
         d = super().dict(*args, **kwargs)
         # Keep these fields as ToastAction so they can be serialized specially
         if "action" in d:
@@ -208,7 +208,12 @@ class Toaster(Component):
     # Pauses toast timers when the page is hidden, e.g., when the tab is backgrounded, the browser is minimized, or the OS is locked.
     pause_when_page_is_hidden: Var[bool]
 
-    def _get_hooks(self) -> Var[str]:
+    def add_hooks(self) -> list[Var | str]:
+        """Add hooks for the toaster component.
+
+        Returns:
+            The hooks for the toaster component.
+        """
         hook = Var.create_safe(
             f"{toast_ref} = toast",
             _var_is_local=True,
@@ -219,7 +224,7 @@ class Toaster(Component):
                 }
             ),
         )
-        return hook
+        return [hook]
 
     @staticmethod
     def send_toast(message: str, level: str | None = None, **props) -> EventSpec:
@@ -235,13 +240,13 @@ class Toaster(Component):
         """
         toast_command = f"{toast_ref}.{level}" if level is not None else toast_ref
         if props:
-            args = serialize(ToastProps(**props))
+            args = serialize(ToastProps(**props))  # type: ignore
             toast = f"{toast_command}(`{message}`, {args})"
         else:
             toast = f"{toast_command}(`{message}`)"
 
-        toast_action = Var.create(toast, _var_is_string=False, _var_is_local=True)
-        return call_script(toast_action)  # type: ignore
+        toast_action = Var.create_safe(toast, _var_is_string=False, _var_is_local=True)
+        return call_script(toast_action)
 
     @staticmethod
     def toast_info(message: str, **kwargs):
@@ -295,7 +300,8 @@ class Toaster(Component):
         """
         return Toaster.send_toast(message, level="success", **kwargs)
 
-    def toast_dismiss(self, id: str | None):
+    @staticmethod
+    def toast_dismiss(id: Var | str | None = None):
         """Dismiss a toast.
 
         Args:
@@ -304,12 +310,22 @@ class Toaster(Component):
         Returns:
             The toast dismiss event.
         """
-        if id is None:
-            dismiss = f"{toast_ref}.dismiss()"
+        dismiss_var_data = None
+
+        if isinstance(id, Var):
+            dismiss = f"{toast_ref}.dismiss({id._var_name_unwrapped})"
+            dismiss_var_data = id._var_data
+        elif isinstance(id, str):
+            dismiss = f"{toast_ref}.dismiss('{id}')"
         else:
-            dismiss = f"{toast_ref}.dismiss({id})"
-        dismiss_action = Var.create(dismiss, _var_is_string=False, _var_is_local=True)
-        return call_script(dismiss_action)  # type: ignore
+            dismiss = f"{toast_ref}.dismiss()"
+        dismiss_action = Var.create_safe(
+            dismiss,
+            _var_is_string=False,
+            _var_is_local=True,
+            _var_data=dismiss_var_data,
+        )
+        return call_script(dismiss_action)
 
 
 # TODO: figure out why loading toast stay open forever

+ 8 - 4
reflex/components/sonner/toast.pyi

@@ -46,7 +46,7 @@ class ToastProps(PropsBase):
     dismissible: Optional[bool]
     action: Optional[ToastAction]
     cancel: Optional[ToastAction]
-    id: Optional[str]
+    id: Optional[Union[str, Var]]
     unstyled: Optional[bool]
     style: Optional[Style]
     action_button_styles: Optional[Style]
@@ -54,9 +54,10 @@ class ToastProps(PropsBase):
     on_dismiss: Optional[Any]
     on_auto_close: Optional[Any]
 
-    def dict(self, *args, **kwargs) -> dict: ...
+    def dict(self, *args, **kwargs) -> dict[str, Any]: ...
 
 class Toaster(Component):
+    def add_hooks(self) -> list[Var | str]: ...
     @staticmethod
     def send_toast(message: str, level: str | None = None, **props) -> EventSpec: ...
     @staticmethod
@@ -67,7 +68,8 @@ class Toaster(Component):
     def toast_error(message: str, **kwargs): ...
     @staticmethod
     def toast_success(message: str, **kwargs): ...
-    def toast_dismiss(self, id: str | None): ...
+    @staticmethod
+    def toast_dismiss(id: Var | str | None = None): ...
     @overload
     @classmethod
     def create(  # type: ignore
@@ -202,7 +204,9 @@ class ToastNamespace(ComponentNamespace):
     dismiss = staticmethod(Toaster.toast_dismiss)
 
     @staticmethod
-    def __call__(message: str, level: Optional[str], **props) -> "Optional[EventSpec]":
+    def __call__(
+        message: str, level: Optional[str] = None, **props
+    ) -> "Optional[EventSpec]":
         """Send a toast message.
 
         Args:

+ 1 - 1
reflex/event.py

@@ -714,7 +714,7 @@ def _callback_arg_spec(eval_result):
 
 
 def call_script(
-    javascript_code: str,
+    javascript_code: str | Var[str],
     callback: EventSpec
     | EventHandler
     | Callable

+ 22 - 2
reflex/experimental/__init__.py

@@ -17,7 +17,28 @@ warn(
     "`rx._x` contains experimental features and might be removed at any time in the future .",
 )
 
-_x = SimpleNamespace(
+_EMITTED_PROMOTION_WARNINGS = set()
+
+
+class ExperimentalNamespace(SimpleNamespace):
+    """Namespace for experimental features."""
+
+    @property
+    def toast(self):
+        """Temporary property returning the toast namespace.
+
+        Remove this property when toast is fully promoted.
+
+        Returns:
+            The toast namespace.
+        """
+        if "toast" not in _EMITTED_PROMOTION_WARNINGS:
+            _EMITTED_PROMOTION_WARNINGS.add("toast")
+            warn(f"`rx._x.toast` was promoted to `rx.toast`.")
+        return toast
+
+
+_x = ExperimentalNamespace(
     asset=asset,
     client_state=ClientStateVar.create,
     hooks=hooks,
@@ -25,5 +46,4 @@ _x = SimpleNamespace(
     progress=progress,
     PropsBase=PropsBase,
     run_in_thread=run_in_thread,
-    toast=toast,
 )

+ 3 - 1
reflex/utils/pyi_generator.py

@@ -488,7 +488,9 @@ def _generate_staticmethod_call_functiondef(
         kwonlyargs=[],
         kw_defaults=[],
         kwarg=ast.arg(arg="props"),
-        defaults=[],
+        defaults=[ast.Constant(value=default) for default in fullspec.defaults]
+        if fullspec.defaults
+        else [],
     )
     definition = ast.FunctionDef(
         name="__call__",