Compare commits
6 Commits
release/re
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
98f50811f9 | ||
![]() |
ee03415894 | ||
![]() |
8943341605 | ||
![]() |
836e8f8ce9 | ||
![]() |
e891dbe6b9 | ||
![]() |
c2917d46d5 |
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "reflex"
|
||||
version = "0.7.1dev1"
|
||||
version = "0.7.2dev1"
|
||||
description = "Web apps in pure Python."
|
||||
license = "Apache-2.0"
|
||||
authors = [
|
||||
|
@ -15,7 +15,13 @@
|
||||
"devDependencies": {
|
||||
{% for package, version in dev_dependencies.items() %}
|
||||
"{{ package }}": "{{ version }}"{% if not loop.last %},{% endif %}
|
||||
|
||||
|
||||
{% endfor %}
|
||||
},
|
||||
"overrides": {
|
||||
{% for package, version in overrides.items() %}
|
||||
"{{ package }}": "{{ version }}"{% if not loop.last %},{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
}
|
||||
}
|
@ -78,9 +78,9 @@ export function UploadFilesProvider({ children }) {
|
||||
return newFilesById
|
||||
})
|
||||
return (
|
||||
<UploadFilesContext.Provider value={[filesById, setFilesById]}>
|
||||
<UploadFilesContext value={[filesById, setFilesById]}>
|
||||
{children}
|
||||
</UploadFilesContext.Provider>
|
||||
</UploadFilesContext>
|
||||
)
|
||||
}
|
||||
|
||||
@ -92,9 +92,9 @@ export function EventLoopProvider({ children }) {
|
||||
clientStorage,
|
||||
)
|
||||
return (
|
||||
<EventLoopContext.Provider value={[addEvents, connectErrors]}>
|
||||
<EventLoopContext value={[addEvents, connectErrors]}>
|
||||
{children}
|
||||
</EventLoopContext.Provider>
|
||||
</EventLoopContext>
|
||||
)
|
||||
}
|
||||
|
||||
@ -112,13 +112,13 @@ export function StateProvider({ children }) {
|
||||
|
||||
return (
|
||||
{% for state_name in initial_state %}
|
||||
<StateContexts.{{state_name|var_name}}.Provider value={ {{state_name|var_name}} }>
|
||||
<StateContexts.{{state_name|var_name}} value={ {{state_name|var_name}} }>
|
||||
{% endfor %}
|
||||
<DispatchContext.Provider value={dispatchers}>
|
||||
<DispatchContext value={dispatchers}>
|
||||
{children}
|
||||
</DispatchContext.Provider>
|
||||
</DispatchContext>
|
||||
{% for state_name in initial_state|reverse %}
|
||||
</StateContexts.{{state_name|var_name}}.Provider>
|
||||
</StateContexts.{{state_name|var_name}}>
|
||||
{% endfor %}
|
||||
)
|
||||
}
|
||||
|
@ -36,17 +36,17 @@ export default function RadixThemesColorModeProvider({ children }) {
|
||||
const allowedModes = ["light", "dark", "system"];
|
||||
if (!allowedModes.includes(mode)) {
|
||||
console.error(
|
||||
`Invalid color mode "${mode}". Defaulting to "${defaultColorMode}".`
|
||||
`Invalid color mode "${mode}". Defaulting to "${defaultColorMode}".`,
|
||||
);
|
||||
mode = defaultColorMode;
|
||||
}
|
||||
setTheme(mode);
|
||||
};
|
||||
return (
|
||||
<ColorModeContext.Provider
|
||||
<ColorModeContext
|
||||
value={{ rawColorMode, resolvedColorMode, toggleColorMode, setColorMode }}
|
||||
>
|
||||
{children}
|
||||
</ColorModeContext.Provider>
|
||||
</ColorModeContext>
|
||||
);
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ from reflex.components.core.client_side_routing import (
|
||||
from reflex.components.core.sticky import sticky
|
||||
from reflex.components.core.upload import Upload, get_upload_dir
|
||||
from reflex.components.radix import themes
|
||||
from reflex.components.sonner.toast import toast
|
||||
from reflex.config import ExecutorType, environment, get_config
|
||||
from reflex.event import (
|
||||
_EVENT_FIELDS,
|
||||
@ -84,7 +85,6 @@ from reflex.event import (
|
||||
EventType,
|
||||
IndividualEventType,
|
||||
get_hydrate_event,
|
||||
window_alert,
|
||||
)
|
||||
from reflex.model import Model, get_db_status
|
||||
from reflex.page import DECORATED_PAGES
|
||||
@ -144,7 +144,7 @@ def default_backend_exception_handler(exception: Exception) -> EventSpec:
|
||||
EventSpec: The window alert event.
|
||||
|
||||
"""
|
||||
from reflex.components.sonner.toast import Toaster, toast
|
||||
from reflex.components.sonner.toast import toast
|
||||
|
||||
error = traceback.format_exc()
|
||||
|
||||
@ -155,18 +155,16 @@ def default_backend_exception_handler(exception: Exception) -> EventSpec:
|
||||
if is_prod_mode()
|
||||
else [f"{type(exception).__name__}: {exception}.", "See logs for details."]
|
||||
)
|
||||
if Toaster.is_used:
|
||||
return toast(
|
||||
"An error occurred.",
|
||||
level="error",
|
||||
description="<br/>".join(error_message),
|
||||
position="top-center",
|
||||
id="backend_error",
|
||||
style={"width": "500px"},
|
||||
)
|
||||
else:
|
||||
error_message.insert(0, "An error occurred.")
|
||||
return window_alert("\n".join(error_message))
|
||||
|
||||
return toast(
|
||||
"An error occurred.",
|
||||
level="error",
|
||||
fallback_to_alert=True,
|
||||
description="<br/>".join(error_message),
|
||||
position="top-center",
|
||||
id="backend_error",
|
||||
style={"width": "500px"},
|
||||
)
|
||||
|
||||
|
||||
def extra_overlay_function() -> Optional[Component]:
|
||||
@ -414,7 +412,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
] = default_backend_exception_handler
|
||||
|
||||
# Put the toast provider in the app wrap.
|
||||
bundle_toaster: bool = True
|
||||
toaster: Component | None = dataclasses.field(default_factory=toast.provider)
|
||||
|
||||
@property
|
||||
def api(self) -> FastAPI | None:
|
||||
@ -1100,10 +1098,6 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
should_compile = self._should_compile()
|
||||
|
||||
if not should_compile:
|
||||
if self.bundle_toaster:
|
||||
from reflex.components.sonner.toast import Toaster
|
||||
|
||||
Toaster.is_used = True
|
||||
with console.timing("Evaluate Pages (Backend)"):
|
||||
for route in self._unevaluated_pages:
|
||||
console.debug(f"Evaluating page: {route}")
|
||||
@ -1133,20 +1127,6 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
+ adhoc_steps_without_executor,
|
||||
)
|
||||
|
||||
if self.bundle_toaster:
|
||||
from reflex.components.component import memo
|
||||
from reflex.components.sonner.toast import toast
|
||||
|
||||
internal_toast_provider = toast.provider()
|
||||
|
||||
@memo
|
||||
def memoized_toast_provider():
|
||||
return internal_toast_provider
|
||||
|
||||
toast_provider = Fragment.create(memoized_toast_provider())
|
||||
|
||||
app_wrappers[(1, "ToasterProvider")] = toast_provider
|
||||
|
||||
with console.timing("Evaluate Pages (Frontend)"):
|
||||
performance_metrics: list[tuple[str, float]] = []
|
||||
for route in self._unevaluated_pages:
|
||||
@ -1207,6 +1187,17 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
# Add the custom components from the page to the set.
|
||||
custom_components |= component._get_all_custom_components()
|
||||
|
||||
if (toaster := self.toaster) is not None:
|
||||
from reflex.components.component import memo
|
||||
|
||||
@memo
|
||||
def memoized_toast_provider():
|
||||
return toaster
|
||||
|
||||
toast_provider = Fragment.create(memoized_toast_provider())
|
||||
|
||||
app_wrappers[(1, "ToasterProvider")] = toast_provider
|
||||
|
||||
# Add the app wraps to the app.
|
||||
for key, app_wrap in self.app_wraps.items():
|
||||
component = app_wrap(self._state is not None)
|
||||
|
@ -11,7 +11,9 @@ from reflex.vars.base import Var, get_unique_variable_name
|
||||
class AutoScroll(Div):
|
||||
"""A div that automatically scrolls to the bottom when new content is added."""
|
||||
|
||||
_memoization_mode = MemoizationMode(disposition=MemoizationDisposition.ALWAYS)
|
||||
_memoization_mode = MemoizationMode(
|
||||
disposition=MemoizationDisposition.ALWAYS, recursive=False
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def create(cls, *children, **props):
|
||||
@ -44,7 +46,6 @@ class AutoScroll(Div):
|
||||
"""
|
||||
ref_name = self.get_ref()
|
||||
return [
|
||||
"const containerRef = useRef(null);",
|
||||
"const wasNearBottom = useRef(false);",
|
||||
"const hadScrollbar = useRef(false);",
|
||||
f"""
|
||||
@ -85,6 +86,8 @@ useEffect(() => {{
|
||||
const container = {ref_name}.current;
|
||||
if (!container) return;
|
||||
|
||||
scrollToBottomIfNeeded();
|
||||
|
||||
// Create ResizeObserver to detect height changes
|
||||
const resizeObserver = new ResizeObserver(() => {{
|
||||
scrollToBottomIfNeeded();
|
||||
|
@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
from typing import Optional
|
||||
|
||||
from reflex import constants
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.core.cond import cond
|
||||
from reflex.components.el.elements.typography import Div
|
||||
@ -16,7 +17,7 @@ from reflex.components.radix.themes.components.dialog import (
|
||||
)
|
||||
from reflex.components.radix.themes.layout.flex import Flex
|
||||
from reflex.components.radix.themes.typography.text import Text
|
||||
from reflex.components.sonner.toast import Toaster, ToastProps
|
||||
from reflex.components.sonner.toast import ToastProps, toast_ref
|
||||
from reflex.config import environment
|
||||
from reflex.constants import Dirs, Hooks, Imports
|
||||
from reflex.constants.compiler import CompileVars
|
||||
@ -90,7 +91,7 @@ def default_connection_error() -> list[str | Var | Component]:
|
||||
]
|
||||
|
||||
|
||||
class ConnectionToaster(Toaster):
|
||||
class ConnectionToaster(Fragment):
|
||||
"""A connection toaster component."""
|
||||
|
||||
def add_hooks(self) -> list[str | Var]:
|
||||
@ -113,11 +114,11 @@ class ConnectionToaster(Toaster):
|
||||
if environment.REFLEX_DOES_BACKEND_COLD_START.get():
|
||||
loading_message = Var.create("Backend is starting.")
|
||||
backend_is_loading_toast_var = Var(
|
||||
f"toast.loading({loading_message!s}, {{...toast_props, description: '', closeButton: false, onDismiss: () => setUserDismissed(true)}},)"
|
||||
f"toast?.loading({loading_message!s}, {{...toast_props, description: '', closeButton: false, onDismiss: () => setUserDismissed(true)}},)"
|
||||
)
|
||||
backend_is_not_responding = Var.create("Backend is not responding.")
|
||||
backend_is_down_toast_var = Var(
|
||||
f"toast.error({backend_is_not_responding!s}, {{...toast_props, description: '', onDismiss: () => setUserDismissed(true)}},)"
|
||||
f"toast?.error({backend_is_not_responding!s}, {{...toast_props, description: '', onDismiss: () => setUserDismissed(true)}},)"
|
||||
)
|
||||
toast_var = Var(
|
||||
f"""
|
||||
@ -138,10 +139,11 @@ setTimeout(() => {{
|
||||
f"Cannot connect to server: {connection_error}."
|
||||
)
|
||||
toast_var = Var(
|
||||
f"toast.error({loading_message!s}, {{...toast_props, onDismiss: () => setUserDismissed(true)}},)"
|
||||
f"toast?.error({loading_message!s}, {{...toast_props, onDismiss: () => setUserDismissed(true)}},)"
|
||||
)
|
||||
|
||||
individual_hooks = [
|
||||
Var(f"const toast = {toast_ref};"),
|
||||
f"const toast_props = {LiteralVar.create(props)!s};",
|
||||
"const [userDismissed, setUserDismissed] = useState(false);",
|
||||
"const [waitedForBackend, setWaitedForBackend] = useState(false);",
|
||||
@ -163,7 +165,7 @@ setTimeout(() => {{
|
||||
{toast_var!s}
|
||||
}}
|
||||
}} else {{
|
||||
toast.dismiss("{toast_id}");
|
||||
toast?.dismiss("{toast_id}");
|
||||
setUserDismissed(false); // after reconnection reset dismissed state
|
||||
}}
|
||||
}}
|
||||
@ -189,7 +191,6 @@ setTimeout(() => {{
|
||||
Returns:
|
||||
The connection toaster component.
|
||||
"""
|
||||
Toaster.is_used = True
|
||||
return super().create(*children, **props)
|
||||
|
||||
|
||||
|
@ -5,10 +5,10 @@
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Dict, Literal, Optional, Union, overload
|
||||
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.el.elements.typography import Div
|
||||
from reflex.components.lucide.icon import Icon
|
||||
from reflex.components.sonner.toast import Toaster, ToastProps
|
||||
from reflex.constants.compiler import CompileVars
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
@ -41,48 +41,13 @@ class WebsocketTargetURL(Var):
|
||||
|
||||
def default_connection_error() -> list[str | Var | Component]: ...
|
||||
|
||||
class ConnectionToaster(Toaster):
|
||||
class ConnectionToaster(Fragment):
|
||||
def add_hooks(self) -> list[str | Var]: ...
|
||||
@overload
|
||||
@classmethod
|
||||
def create( # type: ignore
|
||||
cls,
|
||||
*children,
|
||||
theme: Optional[Union[Var[str], str]] = None,
|
||||
rich_colors: Optional[Union[Var[bool], bool]] = None,
|
||||
expand: Optional[Union[Var[bool], bool]] = None,
|
||||
visible_toasts: Optional[Union[Var[int], int]] = None,
|
||||
position: Optional[
|
||||
Union[
|
||||
Literal[
|
||||
"bottom-center",
|
||||
"bottom-left",
|
||||
"bottom-right",
|
||||
"top-center",
|
||||
"top-left",
|
||||
"top-right",
|
||||
],
|
||||
Var[
|
||||
Literal[
|
||||
"bottom-center",
|
||||
"bottom-left",
|
||||
"bottom-right",
|
||||
"top-center",
|
||||
"top-left",
|
||||
"top-right",
|
||||
]
|
||||
],
|
||||
]
|
||||
] = None,
|
||||
close_button: Optional[Union[Var[bool], bool]] = None,
|
||||
offset: Optional[Union[Var[str], str]] = None,
|
||||
dir: Optional[Union[Var[str], str]] = None,
|
||||
hotkey: Optional[Union[Var[str], str]] = None,
|
||||
invert: Optional[Union[Var[bool], bool]] = None,
|
||||
toast_options: Optional[Union[ToastProps, Var[ToastProps]]] = None,
|
||||
gap: Optional[Union[Var[int], int]] = None,
|
||||
loading_icon: Optional[Union[Icon, Var[Icon]]] = None,
|
||||
pause_when_page_is_hidden: Optional[Union[Var[bool], bool]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
id: Optional[Any] = None,
|
||||
@ -110,20 +75,6 @@ class ConnectionToaster(Toaster):
|
||||
|
||||
Args:
|
||||
*children: The children of the component.
|
||||
theme: the theme of the toast
|
||||
rich_colors: whether to show rich colors
|
||||
expand: whether to expand the toast
|
||||
visible_toasts: the number of toasts that are currently visible
|
||||
position: the position of the toast
|
||||
close_button: whether to show the close button
|
||||
offset: offset of the toast
|
||||
dir: directionality of the toast (default: ltr)
|
||||
hotkey: Keyboard shortcut that will move focus to the toaster area.
|
||||
invert: Dark toasts in light mode and vice versa.
|
||||
toast_options: These will act as default options for all toasts. See toast() for all available options.
|
||||
gap: Gap between toasts when expanded
|
||||
loading_icon: Changes the default loading icon
|
||||
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.
|
||||
style: The style of the component.
|
||||
key: A unique key for the component.
|
||||
id: The id for the component.
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, ClassVar, Literal, Optional, Union
|
||||
from typing import Any, Literal, Optional, Union
|
||||
|
||||
from reflex.base import Base
|
||||
from reflex.components.component import Component, ComponentNamespace
|
||||
@ -17,6 +17,7 @@ from reflex.utils.serializers import serializer
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import LiteralVar, Var
|
||||
from reflex.vars.function import FunctionVar
|
||||
from reflex.vars.number import ternary_operation
|
||||
from reflex.vars.object import ObjectVar
|
||||
|
||||
LiteralPosition = Literal[
|
||||
@ -217,9 +218,6 @@ 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]
|
||||
|
||||
# Marked True when any Toast component is created.
|
||||
is_used: ClassVar[bool] = False
|
||||
|
||||
def add_hooks(self) -> list[Var | str]:
|
||||
"""Add hooks for the toaster component.
|
||||
|
||||
@ -241,13 +239,17 @@ class Toaster(Component):
|
||||
|
||||
@staticmethod
|
||||
def send_toast(
|
||||
message: str | Var = "", level: str | None = None, **props
|
||||
message: str | Var = "",
|
||||
level: str | None = None,
|
||||
fallback_to_alert: bool = False,
|
||||
**props,
|
||||
) -> EventSpec:
|
||||
"""Send a toast message.
|
||||
|
||||
Args:
|
||||
message: The message to display.
|
||||
level: The level of the toast.
|
||||
fallback_to_alert: Whether to fallback to an alert if the toaster is not created.
|
||||
**props: The options for the toast.
|
||||
|
||||
Raises:
|
||||
@ -256,11 +258,6 @@ class Toaster(Component):
|
||||
Returns:
|
||||
The toast event.
|
||||
"""
|
||||
if not Toaster.is_used:
|
||||
raise ValueError(
|
||||
"Toaster component must be created before sending a toast. (use `rx.toast.provider()`)"
|
||||
)
|
||||
|
||||
toast_command = (
|
||||
ObjectVar.__getattr__(toast_ref.to(dict), level) if level else toast_ref
|
||||
).to(FunctionVar)
|
||||
@ -277,6 +274,21 @@ class Toaster(Component):
|
||||
else:
|
||||
toast = toast_command.call(message)
|
||||
|
||||
if fallback_to_alert:
|
||||
toast = ternary_operation(
|
||||
toast_ref.bool(),
|
||||
toast,
|
||||
FunctionVar("window.alert").call(
|
||||
Var.create(
|
||||
message
|
||||
if isinstance(message, str) and message
|
||||
else props.get("title", props.get("description", ""))
|
||||
)
|
||||
.to(str)
|
||||
.replace("<br/>", "\n")
|
||||
),
|
||||
)
|
||||
|
||||
return run_script(toast)
|
||||
|
||||
@staticmethod
|
||||
@ -379,7 +391,6 @@ class Toaster(Component):
|
||||
Returns:
|
||||
The toaster component.
|
||||
"""
|
||||
cls.is_used = True
|
||||
return super().create(*children, **props)
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, ClassVar, Dict, Literal, Optional, Union, overload
|
||||
from typing import Any, Dict, Literal, Optional, Union, overload
|
||||
|
||||
from reflex.base import Base
|
||||
from reflex.components.component import Component, ComponentNamespace
|
||||
@ -60,12 +60,13 @@ class ToastProps(PropsBase, NoExtrasAllowedProps):
|
||||
def dict(self, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
|
||||
|
||||
class Toaster(Component):
|
||||
is_used: ClassVar[bool] = False
|
||||
|
||||
def add_hooks(self) -> list[Var | str]: ...
|
||||
@staticmethod
|
||||
def send_toast(
|
||||
message: str | Var = "", level: str | None = None, **props
|
||||
message: str | Var = "",
|
||||
level: str | None = None,
|
||||
fallback_to_alert: bool = False,
|
||||
**props,
|
||||
) -> EventSpec: ...
|
||||
@staticmethod
|
||||
def toast_info(message: str | Var = "", **kwargs: Any): ...
|
||||
@ -185,13 +186,17 @@ class ToastNamespace(ComponentNamespace):
|
||||
|
||||
@staticmethod
|
||||
def __call__(
|
||||
message: Union[str, Var] = "", level: Optional[str] = None, **props
|
||||
message: Union[str, Var] = "",
|
||||
level: Optional[str] = None,
|
||||
fallback_to_alert: bool = False,
|
||||
**props,
|
||||
) -> "EventSpec":
|
||||
"""Send a toast message.
|
||||
|
||||
Args:
|
||||
message: The message to display.
|
||||
level: The level of the toast.
|
||||
fallback_to_alert: Whether to fallback to an alert if the toaster is not created.
|
||||
**props: The options for the toast.
|
||||
|
||||
Raises:
|
||||
|
@ -195,3 +195,7 @@ class PackageJson(SimpleNamespace):
|
||||
"postcss": "8.5.1",
|
||||
"postcss-import": "16.1.0",
|
||||
}
|
||||
OVERRIDES = {
|
||||
# This should always match the `react` version in DEPENDENCIES for recharts compatibility.
|
||||
"react-is": "19.0.0"
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ from typing import Any, Literal, Tuple, Type
|
||||
|
||||
from reflex import constants
|
||||
from reflex.components.core.breakpoints import Breakpoints, breakpoints_values
|
||||
from reflex.event import EventChain, EventHandler
|
||||
from reflex.event import EventChain, EventHandler, EventSpec, run_script
|
||||
from reflex.utils import format
|
||||
from reflex.utils.exceptions import ReflexError
|
||||
from reflex.utils.imports import ImportVar
|
||||
@ -49,9 +49,9 @@ def _color_mode_var(_js_expr: str, _var_type: Type = str) -> Var:
|
||||
|
||||
|
||||
def set_color_mode(
|
||||
new_color_mode: LiteralColorMode | Var[LiteralColorMode] | None = None,
|
||||
) -> Var[EventChain]:
|
||||
"""Create an EventChain Var that sets the color mode to a specific value.
|
||||
new_color_mode: LiteralColorMode | Var[LiteralColorMode],
|
||||
) -> EventSpec:
|
||||
"""Create an EventSpec Var that sets the color mode to a specific value.
|
||||
|
||||
Note: `set_color_mode` is not a real event and cannot be triggered from a
|
||||
backend event handler.
|
||||
@ -60,24 +60,15 @@ def set_color_mode(
|
||||
new_color_mode: The color mode to set.
|
||||
|
||||
Returns:
|
||||
The EventChain Var that can be passed to an event trigger.
|
||||
The EventSpec Var that can be passed to an event trigger.
|
||||
"""
|
||||
base_setter = _color_mode_var(
|
||||
_js_expr=constants.ColorMode.SET,
|
||||
_var_type=EventChain,
|
||||
).to(FunctionVar)
|
||||
|
||||
return run_script(
|
||||
base_setter.call(new_color_mode),
|
||||
)
|
||||
if new_color_mode is None:
|
||||
return base_setter
|
||||
|
||||
if not isinstance(new_color_mode, Var):
|
||||
new_color_mode = LiteralVar.create(new_color_mode)
|
||||
|
||||
return Var(
|
||||
f"() => {base_setter!s}({new_color_mode!s})",
|
||||
_var_data=VarData.merge(
|
||||
base_setter._get_all_var_data(), new_color_mode._get_all_var_data()
|
||||
),
|
||||
).to(FunctionVar, EventChain)
|
||||
|
||||
|
||||
# Var resolves to the current color mode for the app ("light", "dark" or "system")
|
||||
|
@ -846,6 +846,7 @@ def _compile_package_json():
|
||||
},
|
||||
dependencies=constants.PackageJson.DEPENDENCIES,
|
||||
dev_dependencies=constants.PackageJson.DEV_DEPENDENCIES,
|
||||
overrides=constants.PackageJson.OVERRIDES,
|
||||
)
|
||||
|
||||
|
||||
|
@ -61,7 +61,7 @@ def ColorToggleApp():
|
||||
rx.icon(tag="moon", size=20),
|
||||
value="dark",
|
||||
),
|
||||
on_change=set_color_mode(),
|
||||
on_change=set_color_mode, # pyright: ignore[reportArgumentType]
|
||||
variant="classic",
|
||||
radius="large",
|
||||
value=color_mode,
|
||||
|
@ -35,7 +35,6 @@ import reflex.config
|
||||
from reflex import constants
|
||||
from reflex.app import App
|
||||
from reflex.base import Base
|
||||
from reflex.components.sonner.toast import Toaster
|
||||
from reflex.constants import CompileVars, RouteVar, SocketEvent
|
||||
from reflex.event import Event, EventHandler
|
||||
from reflex.state import (
|
||||
@ -1613,29 +1612,20 @@ async def test_state_with_invalid_yield(capsys, mock_app):
|
||||
rx.event.Event(token="fake_token", name="invalid_handler")
|
||||
):
|
||||
assert not update.delta
|
||||
if Toaster.is_used:
|
||||
assert update.events == rx.event.fix_events(
|
||||
[
|
||||
rx.toast(
|
||||
"An error occurred.",
|
||||
description="TypeError: Your handler test_state_with_invalid_yield.<locals>.StateWithInvalidYield.invalid_handler must only return/yield: None, Events or other EventHandlers referenced by their class (not using `self`).<br/>See logs for details.",
|
||||
level="error",
|
||||
id="backend_error",
|
||||
position="top-center",
|
||||
style={"width": "500px"},
|
||||
)
|
||||
],
|
||||
token="",
|
||||
)
|
||||
else:
|
||||
assert update.events == rx.event.fix_events(
|
||||
[
|
||||
rx.window_alert(
|
||||
"An error occurred.\nContact the website administrator."
|
||||
)
|
||||
],
|
||||
token="",
|
||||
)
|
||||
assert update.events == rx.event.fix_events(
|
||||
[
|
||||
rx.toast(
|
||||
"An error occurred.",
|
||||
level="error",
|
||||
fallback_to_alert=True,
|
||||
description="TypeError: Your handler test_state_with_invalid_yield.<locals>.StateWithInvalidYield.invalid_handler must only return/yield: None, Events or other EventHandlers referenced by their class (not using `self`).<br/>See logs for details.",
|
||||
id="backend_error",
|
||||
position="top-center",
|
||||
style={"width": "500px"},
|
||||
)
|
||||
],
|
||||
token="",
|
||||
)
|
||||
captured = capsys.readouterr()
|
||||
assert "must only return/yield: None, Events or other EventHandlers" in captured.out
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user