merge main

This commit is contained in:
Tom Gotsman 2024-11-01 09:43:30 -07:00
commit 088dcbf1e6
93 changed files with 1940 additions and 632 deletions

View File

@ -228,7 +228,7 @@ You can create a multi-page app by adding more pages.
<div align="center"> <div align="center">
📑 [Docs](https://reflex.dev/docs/getting-started/introduction) &nbsp; | &nbsp; 🗞️ [Blog](https://reflex.dev/blog) &nbsp; | &nbsp; 📱 [Component Library](https://reflex.dev/docs/library) &nbsp; | &nbsp; 🖼️ [Gallery](https://reflex.dev/docs/gallery) &nbsp; | &nbsp; 🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start) &nbsp; 📑 [Docs](https://reflex.dev/docs/getting-started/introduction) &nbsp; | &nbsp; 🗞️ [Blog](https://reflex.dev/blog) &nbsp; | &nbsp; 📱 [Component Library](https://reflex.dev/docs/library) &nbsp; | &nbsp; 🖼️ [Templates](https://reflex.dev/templates/) &nbsp; | &nbsp; 🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start) &nbsp;
</div> </div>

View File

@ -42,8 +42,7 @@ def get_lighthouse_scores(directory_path: str | Path) -> dict:
try: try:
for filename in directory_path.iterdir(): for filename in directory_path.iterdir():
if filename.suffix == ".json" and filename.stem != "manifest": if filename.suffix == ".json" and filename.stem != "manifest":
file_path = directory_path / filename data = json.loads(filename.read_text())
data = json.loads(file_path.read_text())
# Extract scores and add them to the dictionary with the filename as key # Extract scores and add them to the dictionary with the filename as key
scores[data["finalUrl"].replace("http://localhost:3000/", "/")] = { scores[data["finalUrl"].replace("http://localhost:3000/", "/")] = {
"performance_score": data["categories"]["performance"]["score"], "performance_score": data["categories"]["performance"]["score"],

View File

@ -36,14 +36,10 @@
{# component: component dictionary #} {# component: component dictionary #}
{% macro render_tag(component) %} {% macro render_tag(component) %}
<{{component.name}} {{- render_props(component.props) }}> <{{component.name}} {{- render_props(component.props) }}>
{%- if component.args is not none -%}
{{- render_arg_content(component) }}
{%- else -%}
{{ component.contents }} {{ component.contents }}
{% for child in component.children %} {% for child in component.children %}
{{ render(child) }} {{ render(child) }}
{% endfor %} {% endfor %}
{%- endif -%}
</{{component.name}}> </{{component.name}}>
{%- endmacro %} {%- endmacro %}

View File

@ -1,26 +1,31 @@
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
import { codeToHtml} from "shiki" import { codeToHtml} from "shiki"
export function Code ({code, theme, language, transformers, ...divProps}) { /**
* Code component that uses Shiki to convert code to HTML and render it.
*
* @param code - The code to be highlighted.
* @param theme - The theme to be used for highlighting.
* @param language - The language of the code.
* @param transformers - The transformers to be applied to the code.
* @param decorations - The decorations to be applied to the code.
* @param divProps - Additional properties to be passed to the div element.
* @returns The rendered code block.
*/
export function Code ({code, theme, language, transformers, decorations, ...divProps}) {
const [codeResult, setCodeResult] = useState("") const [codeResult, setCodeResult] = useState("")
useEffect(() => { useEffect(() => {
async function fetchCode() { async function fetchCode() {
let final_code; const result = await codeToHtml(code, {
if (Array.isArray(code)) {
final_code = code[0];
} else {
final_code = code;
}
const result = await codeToHtml(final_code, {
lang: language, lang: language,
theme, theme,
transformers transformers,
decorations
}); });
setCodeResult(result); setCodeResult(result);
} }
fetchCode(); fetchCode();
}, [code, language, theme, transformers] }, [code, language, theme, transformers, decorations]
) )
return ( return (

View File

@ -15,7 +15,6 @@ import {
} from "$/utils/context.js"; } from "$/utils/context.js";
import debounce from "$/utils/helpers/debounce"; import debounce from "$/utils/helpers/debounce";
import throttle from "$/utils/helpers/throttle"; import throttle from "$/utils/helpers/throttle";
import * as Babel from "@babel/standalone";
// Endpoint URLs. // Endpoint URLs.
const EVENTURL = env.EVENT; const EVENTURL = env.EVENT;
@ -139,8 +138,7 @@ export const evalReactComponent = async (component) => {
if (!window.React && window.__reflex) { if (!window.React && window.__reflex) {
window.React = window.__reflex.react; window.React = window.__reflex.react;
} }
const output = Babel.transform(component, { presets: ["react"] }).code; const encodedJs = encodeURIComponent(component);
const encodedJs = encodeURIComponent(output);
const dataUri = "data:text/javascript;charset=utf-8," + encodedJs; const dataUri = "data:text/javascript;charset=utf-8," + encodedJs;
const module = await eval(`import(dataUri)`); const module = await eval(`import(dataUri)`);
return module.default; return module.default;

View File

@ -46,7 +46,6 @@ from starlette_admin.contrib.sqla.view import ModelView
from reflex import constants from reflex import constants
from reflex.admin import AdminDash from reflex.admin import AdminDash
from reflex.app_mixins import AppMixin, LifespanMixin, MiddlewareMixin from reflex.app_mixins import AppMixin, LifespanMixin, MiddlewareMixin
from reflex.base import Base
from reflex.compiler import compiler from reflex.compiler import compiler
from reflex.compiler import utils as compiler_utils from reflex.compiler import utils as compiler_utils
from reflex.compiler.compiler import ( from reflex.compiler.compiler import (
@ -70,7 +69,14 @@ from reflex.components.core.client_side_routing import (
from reflex.components.core.upload import Upload, get_upload_dir from reflex.components.core.upload import Upload, get_upload_dir
from reflex.components.radix import themes from reflex.components.radix import themes
from reflex.config import environment, get_config from reflex.config import environment, get_config
from reflex.event import Event, EventHandler, EventSpec, window_alert from reflex.event import (
Event,
EventHandler,
EventSpec,
EventType,
IndividualEventType,
window_alert,
)
from reflex.model import Model, get_db_status from reflex.model import Model, get_db_status
from reflex.page import ( from reflex.page import (
DECORATED_PAGES, DECORATED_PAGES,
@ -189,11 +195,12 @@ class UnevaluatedPage:
title: Union[Var, str, None] title: Union[Var, str, None]
description: Union[Var, str, None] description: Union[Var, str, None]
image: str image: str
on_load: Union[EventHandler, EventSpec, List[Union[EventHandler, EventSpec]], None] on_load: Union[EventType[[]], None]
meta: List[Dict[str, str]] meta: List[Dict[str, str]]
class App(MiddlewareMixin, LifespanMixin, Base): @dataclasses.dataclass()
class App(MiddlewareMixin, LifespanMixin):
"""The main Reflex app that encapsulates the backend and frontend. """The main Reflex app that encapsulates the backend and frontend.
Every Reflex app needs an app defined in its main module. Every Reflex app needs an app defined in its main module.
@ -215,24 +222,26 @@ class App(MiddlewareMixin, LifespanMixin, Base):
""" """
# The global [theme](https://reflex.dev/docs/styling/theming/#theme) for the entire app. # The global [theme](https://reflex.dev/docs/styling/theming/#theme) for the entire app.
theme: Optional[Component] = themes.theme(accent_color="blue") theme: Optional[Component] = dataclasses.field(
default_factory=lambda: themes.theme(accent_color="blue")
)
# The [global style](https://reflex.dev/docs/styling/overview/#global-styles}) for the app. # The [global style](https://reflex.dev/docs/styling/overview/#global-styles}) for the app.
style: ComponentStyle = {} style: ComponentStyle = dataclasses.field(default_factory=dict)
# A list of URLs to [stylesheets](https://reflex.dev/docs/styling/custom-stylesheets/) to include in the app. # A list of URLs to [stylesheets](https://reflex.dev/docs/styling/custom-stylesheets/) to include in the app.
stylesheets: List[str] = [] stylesheets: List[str] = dataclasses.field(default_factory=list)
# A component that is present on every page (defaults to the Connection Error banner). # A component that is present on every page (defaults to the Connection Error banner).
overlay_component: Optional[Union[Component, ComponentCallable]] = ( overlay_component: Optional[Union[Component, ComponentCallable]] = (
default_overlay_component() dataclasses.field(default_factory=default_overlay_component)
) )
# Error boundary component to wrap the app with. # Error boundary component to wrap the app with.
error_boundary: Optional[ComponentCallable] = default_error_boundary error_boundary: Optional[ComponentCallable] = default_error_boundary
# Components to add to the head of every page. # Components to add to the head of every page.
head_components: List[Component] = [] head_components: List[Component] = dataclasses.field(default_factory=list)
# The Socket.IO AsyncServer instance. # The Socket.IO AsyncServer instance.
sio: Optional[AsyncServer] = None sio: Optional[AsyncServer] = None
@ -244,10 +253,12 @@ class App(MiddlewareMixin, LifespanMixin, Base):
html_custom_attrs: Optional[Dict[str, str]] = None html_custom_attrs: Optional[Dict[str, str]] = None
# A map from a route to an unevaluated page. PRIVATE. # A map from a route to an unevaluated page. PRIVATE.
unevaluated_pages: Dict[str, UnevaluatedPage] = {} unevaluated_pages: Dict[str, UnevaluatedPage] = dataclasses.field(
default_factory=dict
)
# A map from a page route to the component to render. Users should use `add_page`. PRIVATE. # A map from a page route to the component to render. Users should use `add_page`. PRIVATE.
pages: Dict[str, Component] = {} pages: Dict[str, Component] = dataclasses.field(default_factory=dict)
# The backend API object. PRIVATE. # The backend API object. PRIVATE.
api: FastAPI = None # type: ignore api: FastAPI = None # type: ignore
@ -259,7 +270,9 @@ class App(MiddlewareMixin, LifespanMixin, Base):
_state_manager: Optional[StateManager] = None _state_manager: Optional[StateManager] = None
# Mapping from a route to event handlers to trigger when the page loads. PRIVATE. # Mapping from a route to event handlers to trigger when the page loads. PRIVATE.
load_events: Dict[str, List[Union[EventHandler, EventSpec]]] = {} load_events: Dict[str, List[IndividualEventType[[]]]] = dataclasses.field(
default_factory=dict
)
# Admin dashboard to view and manage the database. PRIVATE. # Admin dashboard to view and manage the database. PRIVATE.
admin_dash: Optional[AdminDash] = None admin_dash: Optional[AdminDash] = None
@ -268,7 +281,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
event_namespace: Optional[EventNamespace] = None event_namespace: Optional[EventNamespace] = None
# Background tasks that are currently running. PRIVATE. # Background tasks that are currently running. PRIVATE.
background_tasks: Set[asyncio.Task] = set() background_tasks: Set[asyncio.Task] = dataclasses.field(default_factory=set)
# Frontend Error Handler Function # Frontend Error Handler Function
frontend_exception_handler: Callable[[Exception], None] = ( frontend_exception_handler: Callable[[Exception], None] = (
@ -280,23 +293,14 @@ class App(MiddlewareMixin, LifespanMixin, Base):
[Exception], Union[EventSpec, List[EventSpec], None] [Exception], Union[EventSpec, List[EventSpec], None]
] = default_backend_exception_handler ] = default_backend_exception_handler
def __init__(self, **kwargs): def __post_init__(self):
"""Initialize the app. """Initialize the app.
Args:
**kwargs: Kwargs to initialize the app with.
Raises: Raises:
ValueError: If the event namespace is not provided in the config. ValueError: If the event namespace is not provided in the config.
Also, if there are multiple client subclasses of rx.BaseState(Subclasses of rx.BaseState should consist Also, if there are multiple client subclasses of rx.BaseState(Subclasses of rx.BaseState should consist
of the DefaultState and the client app state). of the DefaultState and the client app state).
""" """
if "connect_error_component" in kwargs:
raise ValueError(
"`connect_error_component` is deprecated, use `overlay_component` instead"
)
super().__init__(**kwargs)
# Special case to allow test cases have multiple subclasses of rx.BaseState. # Special case to allow test cases have multiple subclasses of rx.BaseState.
if not is_testing_env() and BaseState.__subclasses__() != [State]: if not is_testing_env() and BaseState.__subclasses__() != [State]:
# Only rx.State is allowed as Base State subclass. # Only rx.State is allowed as Base State subclass.
@ -471,9 +475,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
title: str | Var | None = None, title: str | Var | None = None,
description: str | Var | None = None, description: str | Var | None = None,
image: str = constants.DefaultPage.IMAGE, image: str = constants.DefaultPage.IMAGE,
on_load: ( on_load: EventType[[]] | None = None,
EventHandler | EventSpec | list[EventHandler | EventSpec] | None
) = None,
meta: list[dict[str, str]] = constants.DefaultPage.META_LIST, meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
): ):
"""Add a page to the app. """Add a page to the app.
@ -559,7 +561,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
self._check_routes_conflict(route) self._check_routes_conflict(route)
self.pages[route] = component self.pages[route] = component
def get_load_events(self, route: str) -> list[EventHandler | EventSpec]: def get_load_events(self, route: str) -> list[IndividualEventType[[]]]:
"""Get the load events for a route. """Get the load events for a route.
Args: Args:
@ -618,9 +620,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
title: str = constants.Page404.TITLE, title: str = constants.Page404.TITLE,
image: str = constants.Page404.IMAGE, image: str = constants.Page404.IMAGE,
description: str = constants.Page404.DESCRIPTION, description: str = constants.Page404.DESCRIPTION,
on_load: ( on_load: EventType[[]] | None = None,
EventHandler | EventSpec | list[EventHandler | EventSpec] | None
) = None,
meta: list[dict[str, str]] = constants.DefaultPage.META_LIST, meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
): ):
"""Define a custom 404 page for any url having no match. """Define a custom 404 page for any url having no match.
@ -929,6 +929,8 @@ class App(MiddlewareMixin, LifespanMixin, Base):
) )
compile_results.append((stateful_components_path, stateful_components_code)) compile_results.append((stateful_components_path, stateful_components_code))
progress.advance(task)
# Compile the root document before fork. # Compile the root document before fork.
compile_results.append( compile_results.append(
compiler.compile_document_root( compiler.compile_document_root(
@ -1389,7 +1391,7 @@ def upload(app: App):
if isinstance(func, EventHandler): if isinstance(func, EventHandler):
if func.is_background: if func.is_background:
raise UploadTypeError( raise UploadTypeError(
f"@rx.background is not supported for upload handler `{handler}`.", f"@rx.event(background=True) is not supported for upload handler `{handler}`.",
) )
func = func.fn func = func.fn
if isinstance(func, functools.partial): if isinstance(func, functools.partial):

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import asyncio import asyncio
import contextlib import contextlib
import dataclasses
import functools import functools
import inspect import inspect
from typing import Callable, Coroutine, Set, Union from typing import Callable, Coroutine, Set, Union
@ -16,11 +17,14 @@ from reflex.utils.exceptions import InvalidLifespanTaskType
from .mixin import AppMixin from .mixin import AppMixin
@dataclasses.dataclass
class LifespanMixin(AppMixin): class LifespanMixin(AppMixin):
"""A Mixin that allow tasks to run during the whole app lifespan.""" """A Mixin that allow tasks to run during the whole app lifespan."""
# Lifespan tasks that are planned to run. # Lifespan tasks that are planned to run.
lifespan_tasks: Set[Union[asyncio.Task, Callable]] = set() lifespan_tasks: Set[Union[asyncio.Task, Callable]] = dataclasses.field(
default_factory=set
)
@contextlib.asynccontextmanager @contextlib.asynccontextmanager
async def _run_lifespan_tasks(self, app: FastAPI): async def _run_lifespan_tasks(self, app: FastAPI):

View File

@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
import dataclasses
from typing import List from typing import List
from reflex.event import Event from reflex.event import Event
@ -12,11 +13,12 @@ from reflex.state import BaseState, StateUpdate
from .mixin import AppMixin from .mixin import AppMixin
@dataclasses.dataclass
class MiddlewareMixin(AppMixin): class MiddlewareMixin(AppMixin):
"""Middleware Mixin that allow to add middleware to the app.""" """Middleware Mixin that allow to add middleware to the app."""
# Middleware to add to the app. Users should use `add_middleware`. PRIVATE. # Middleware to add to the app. Users should use `add_middleware`. PRIVATE.
middleware: List[Middleware] = [] middleware: List[Middleware] = dataclasses.field(default_factory=list)
def _init_mixin(self): def _init_mixin(self):
self.middleware.append(HydrateMiddleware()) self.middleware.append(HydrateMiddleware())

View File

@ -1,9 +1,10 @@
"""Default mixin for all app mixins.""" """Default mixin for all app mixins."""
from reflex.base import Base import dataclasses
class AppMixin(Base): @dataclasses.dataclass
class AppMixin:
"""Define the base class for all app mixins.""" """Define the base class for all app mixins."""
def _init_mixin(self): def _init_mixin(self):

View File

@ -4,10 +4,11 @@ from __future__ import annotations
from typing import Any, Iterator from typing import Any, Iterator
from reflex.components.component import Component from reflex.components.component import Component, LiteralComponentVar
from reflex.components.tags import Tag from reflex.components.tags import Tag
from reflex.components.tags.tagless import Tagless from reflex.components.tags.tagless import Tagless
from reflex.vars import ArrayVar, BooleanVar, ObjectVar, Var from reflex.utils.imports import ParsedImportDict
from reflex.vars import BooleanVar, ObjectVar, Var
class Bare(Component): class Bare(Component):
@ -31,9 +32,77 @@ class Bare(Component):
contents = str(contents) if contents is not None else "" contents = str(contents) if contents is not None else ""
return cls(contents=contents) # type: ignore return cls(contents=contents) # type: ignore
def _get_all_hooks_internal(self) -> dict[str, None]:
"""Include the hooks for the component.
Returns:
The hooks for the component.
"""
hooks = super()._get_all_hooks_internal()
if isinstance(self.contents, LiteralComponentVar):
hooks |= self.contents._var_value._get_all_hooks_internal()
return hooks
def _get_all_hooks(self) -> dict[str, None]:
"""Include the hooks for the component.
Returns:
The hooks for the component.
"""
hooks = super()._get_all_hooks()
if isinstance(self.contents, LiteralComponentVar):
hooks |= self.contents._var_value._get_all_hooks()
return hooks
def _get_all_imports(self) -> ParsedImportDict:
"""Include the imports for the component.
Returns:
The imports for the component.
"""
imports = super()._get_all_imports()
if isinstance(self.contents, LiteralComponentVar):
var_data = self.contents._get_all_var_data()
if var_data:
imports |= {k: list(v) for k, v in var_data.imports}
return imports
def _get_all_dynamic_imports(self) -> set[str]:
"""Get dynamic imports for the component.
Returns:
The dynamic imports.
"""
dynamic_imports = super()._get_all_dynamic_imports()
if isinstance(self.contents, LiteralComponentVar):
dynamic_imports |= self.contents._var_value._get_all_dynamic_imports()
return dynamic_imports
def _get_all_custom_code(self) -> set[str]:
"""Get custom code for the component.
Returns:
The custom code.
"""
custom_code = super()._get_all_custom_code()
if isinstance(self.contents, LiteralComponentVar):
custom_code |= self.contents._var_value._get_all_custom_code()
return custom_code
def _get_all_refs(self) -> set[str]:
"""Get the refs for the children of the component.
Returns:
The refs for the children.
"""
refs = super()._get_all_refs()
if isinstance(self.contents, LiteralComponentVar):
refs |= self.contents._var_value._get_all_refs()
return refs
def _render(self) -> Tag: def _render(self) -> Tag:
if isinstance(self.contents, Var): if isinstance(self.contents, Var):
if isinstance(self.contents, (BooleanVar, ObjectVar, ArrayVar)): if isinstance(self.contents, (BooleanVar, ObjectVar)):
return Tagless(contents=f"{{{str(self.contents.to_string())}}}") return Tagless(contents=f"{{{str(self.contents.to_string())}}}")
return Tagless(contents=f"{{{str(self.contents)}}}") return Tagless(contents=f"{{{str(self.contents)}}}")
return Tagless(contents=str(self.contents)) return Tagless(contents=str(self.contents))

View File

@ -50,6 +50,7 @@ class ErrorBoundary(Component):
Args: Args:
*children: The children of the component. *children: The children of the component.
on_error: Fired when the boundary catches an error.
Fallback_component: Rendered instead of the children when an error is caught. Fallback_component: Rendered instead of the children when an error is caught.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.

View File

@ -65,6 +65,9 @@ class Script(Component):
*children: The children of the component. *children: The children of the component.
src: Required unless inline script is used src: Required unless inline script is used
strategy: When the script will execute: afterInteractive (defer-like behavior) | beforeInteractive | lazyOnload (async-like behavior) strategy: When the script will execute: afterInteractive (defer-like behavior) | beforeInteractive | lazyOnload (async-like behavior)
on_load: Triggered when the script is loading
on_ready: Triggered when the script has loaded
on_error: Triggered when the script has errored
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
import copy import copy
import dataclasses
import typing import typing
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from functools import lru_cache, wraps from functools import lru_cache, wraps
@ -59,7 +60,15 @@ from reflex.utils.imports import (
parse_imports, parse_imports,
) )
from reflex.vars import VarData from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var from reflex.vars.base import (
CachedVarOperation,
LiteralVar,
Var,
cached_property_no_lock,
)
from reflex.vars.function import ArgsFunctionOperation, FunctionStringVar
from reflex.vars.number import ternary_operation
from reflex.vars.object import ObjectVar
from reflex.vars.sequence import LiteralArrayVar from reflex.vars.sequence import LiteralArrayVar
@ -471,6 +480,7 @@ class Component(BaseComponent, ABC):
kwargs["event_triggers"][key] = self._create_event_chain( kwargs["event_triggers"][key] = self._create_event_chain(
value=value, # type: ignore value=value, # type: ignore
args_spec=component_specific_triggers[key], args_spec=component_specific_triggers[key],
key=key,
) )
# Remove any keys that were added as events. # Remove any keys that were added as events.
@ -531,12 +541,14 @@ class Component(BaseComponent, ABC):
List[Union[EventHandler, EventSpec, EventVar]], List[Union[EventHandler, EventSpec, EventVar]],
Callable, Callable,
], ],
key: Optional[str] = None,
) -> Union[EventChain, Var]: ) -> Union[EventChain, Var]:
"""Create an event chain from a variety of input types. """Create an event chain from a variety of input types.
Args: Args:
args_spec: The args_spec of the event trigger being bound. args_spec: The args_spec of the event trigger being bound.
value: The value to create the event chain from. value: The value to create the event chain from.
key: The key of the event trigger being bound.
Returns: Returns:
The event chain. The event chain.
@ -551,7 +563,7 @@ class Component(BaseComponent, ABC):
elif isinstance(value, EventVar): elif isinstance(value, EventVar):
value = [value] value = [value]
elif issubclass(value._var_type, (EventChain, EventSpec)): elif issubclass(value._var_type, (EventChain, EventSpec)):
return self._create_event_chain(args_spec, value.guess_type()) return self._create_event_chain(args_spec, value.guess_type(), key=key)
else: else:
raise ValueError( raise ValueError(
f"Invalid event chain: {str(value)} of type {value._var_type}" f"Invalid event chain: {str(value)} of type {value._var_type}"
@ -570,10 +582,10 @@ class Component(BaseComponent, ABC):
for v in value: for v in value:
if isinstance(v, (EventHandler, EventSpec)): if isinstance(v, (EventHandler, EventSpec)):
# Call the event handler to get the event. # Call the event handler to get the event.
events.append(call_event_handler(v, args_spec)) events.append(call_event_handler(v, args_spec, key=key))
elif isinstance(v, Callable): elif isinstance(v, Callable):
# Call the lambda to get the event chain. # Call the lambda to get the event chain.
result = call_event_fn(v, args_spec) result = call_event_fn(v, args_spec, key=key)
if isinstance(result, Var): if isinstance(result, Var):
raise ValueError( raise ValueError(
f"Invalid event chain: {v}. Cannot use a Var-returning " f"Invalid event chain: {v}. Cannot use a Var-returning "
@ -590,7 +602,7 @@ class Component(BaseComponent, ABC):
result = call_event_fn(value, args_spec) result = call_event_fn(value, args_spec)
if isinstance(result, Var): if isinstance(result, Var):
# Recursively call this function if the lambda returned an EventChain Var. # Recursively call this function if the lambda returned an EventChain Var.
return self._create_event_chain(args_spec, result) return self._create_event_chain(args_spec, result, key=key)
events = [*result] events = [*result]
# Otherwise, raise an error. # Otherwise, raise an error.
@ -1713,6 +1725,7 @@ class CustomComponent(Component):
args_spec=event_triggers_in_component_declaration.get( args_spec=event_triggers_in_component_declaration.get(
key, empty_event key, empty_event
), ),
key=key,
) )
self.props[format.to_camel_case(key)] = value self.props[format.to_camel_case(key)] = value
continue continue
@ -2345,3 +2358,203 @@ class MemoizationLeaf(Component):
load_dynamic_serializer() load_dynamic_serializer()
class ComponentVar(Var[Component], python_types=BaseComponent):
"""A Var that represents a Component."""
def empty_component() -> Component:
"""Create an empty component.
Returns:
An empty component.
"""
from reflex.components.base.bare import Bare
return Bare.create("")
def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) -> Var:
"""Convert a render dict to a Var.
Args:
tag: The render dict.
imported_names: The names of the imported components.
Returns:
The Var.
"""
if not isinstance(tag, dict):
if isinstance(tag, Component):
return render_dict_to_var(tag.render(), imported_names)
return Var.create(tag)
if "iterable" in tag:
function_return = Var.create(
[
render_dict_to_var(child.render(), imported_names)
for child in tag["children"]
]
)
func = ArgsFunctionOperation.create(
(tag["arg_var_name"], tag["index_var_name"]),
function_return,
)
return FunctionStringVar.create("Array.prototype.map.call").call(
tag["iterable"]
if not isinstance(tag["iterable"], ObjectVar)
else tag["iterable"].items(),
func,
)
if tag["name"] == "match":
element = tag["cond"]
conditionals = tag["default"]
for case in tag["match_cases"][::-1]:
condition = case[0].to_string() == element.to_string()
for pattern in case[1:-1]:
condition = condition | (pattern.to_string() == element.to_string())
conditionals = ternary_operation(
condition,
case[-1],
conditionals,
)
return conditionals
if "cond" in tag:
return ternary_operation(
tag["cond"],
render_dict_to_var(tag["true_value"], imported_names),
render_dict_to_var(tag["false_value"], imported_names)
if tag["false_value"] is not None
else Var.create(None),
)
props = {}
special_props = []
for prop_str in tag["props"]:
if "=" not in prop_str:
special_props.append(Var(prop_str).to(ObjectVar))
continue
prop = prop_str.index("=")
key = prop_str[:prop]
value = prop_str[prop + 2 : -1]
props[key] = value
props = Var.create({Var.create(k): Var(v) for k, v in props.items()})
for prop in special_props:
props = props.merge(prop)
contents = tag["contents"][1:-1] if tag["contents"] else None
raw_tag_name = tag.get("name")
tag_name = Var(raw_tag_name or "Fragment")
tag_name = (
Var.create(raw_tag_name)
if raw_tag_name
and raw_tag_name.split(".")[0] not in imported_names
and raw_tag_name.lower() == raw_tag_name
else tag_name
)
return FunctionStringVar.create(
"jsx",
).call(
tag_name,
props,
*([Var(contents)] if contents is not None else []),
*[render_dict_to_var(child, imported_names) for child in tag["children"]],
)
@dataclasses.dataclass(
eq=False,
frozen=True,
)
class LiteralComponentVar(CachedVarOperation, LiteralVar, ComponentVar):
"""A Var that represents a Component."""
_var_value: BaseComponent = dataclasses.field(default_factory=empty_component)
@cached_property_no_lock
def _cached_var_name(self) -> str:
"""Get the name of the var.
Returns:
The name of the var.
"""
var_data = self._get_all_var_data()
if var_data is not None:
# flatten imports
imported_names = {j.alias or j.name for i in var_data.imports for j in i[1]}
else:
imported_names = set()
return str(render_dict_to_var(self._var_value.render(), imported_names))
@cached_property_no_lock
def _cached_get_all_var_data(self) -> VarData | None:
"""Get the VarData for the var.
Returns:
The VarData for the var.
"""
return VarData.merge(
VarData(
imports={
"@emotion/react": [
ImportVar(tag="jsx"),
],
}
),
VarData(
imports=self._var_value._get_all_imports(),
),
VarData(
imports={
"react": [
ImportVar(tag="Fragment"),
],
}
),
)
def __hash__(self) -> int:
"""Get the hash of the var.
Returns:
The hash of the var.
"""
return hash((self.__class__.__name__, self._js_expr))
@classmethod
def create(
cls,
value: Component,
_var_data: VarData | None = None,
):
"""Create a var from a value.
Args:
value: The value of the var.
_var_data: Additional hooks and imports associated with the Var.
Returns:
The var.
"""
return LiteralComponentVar(
_js_expr="",
_var_type=type(value),
_var_data=_var_data,
_var_value=value,
)

View File

@ -50,6 +50,7 @@ class Clipboard(Fragment):
Args: Args:
*children: The children of the component. *children: The children of the component.
targets: The element ids to attach the event listener to. Defaults to all child components or the document. targets: The element ids to attach the event listener to. Defaults to all child components or the document.
on_paste: Called when the user pastes data into the document. Data is a list of tuples of (mime_type, data). Binary types will be base64 encoded as a data uri.
on_paste_event_actions: Save the original event actions for the on_paste event. on_paste_event_actions: Save the original event actions for the on_paste event.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.

View File

@ -5,11 +5,17 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple
from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf from reflex.components.component import (
Component,
ComponentNamespace,
MemoizationLeaf,
StatefulComponent,
)
from reflex.components.el.elements.forms import Input from reflex.components.el.elements.forms import Input
from reflex.components.radix.themes.layout.box import Box from reflex.components.radix.themes.layout.box import Box
from reflex.config import environment from reflex.config import environment
from reflex.constants import Dirs from reflex.constants import Dirs
from reflex.constants.compiler import Imports
from reflex.event import ( from reflex.event import (
CallableEventSpec, CallableEventSpec,
EventChain, EventChain,
@ -19,9 +25,10 @@ from reflex.event import (
call_script, call_script,
parse_args_spec, parse_args_spec,
) )
from reflex.utils import format
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.vars import VarData from reflex.vars import VarData
from reflex.vars.base import CallableVar, LiteralVar, Var from reflex.vars.base import CallableVar, LiteralVar, Var, get_unique_variable_name
from reflex.vars.sequence import LiteralStringVar from reflex.vars.sequence import LiteralStringVar
DEFAULT_UPLOAD_ID: str = "default" DEFAULT_UPLOAD_ID: str = "default"
@ -179,9 +186,7 @@ class Upload(MemoizationLeaf):
library = "react-dropzone@14.2.10" library = "react-dropzone@14.2.10"
tag = "ReactDropzone" tag = ""
is_default = True
# The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as # The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as
# values. # values.
@ -201,7 +206,7 @@ class Upload(MemoizationLeaf):
min_size: Var[int] min_size: Var[int]
# Whether to allow multiple files to be uploaded. # Whether to allow multiple files to be uploaded.
multiple: Var[bool] = True # type: ignore multiple: Var[bool]
# Whether to disable click to upload. # Whether to disable click to upload.
no_click: Var[bool] no_click: Var[bool]
@ -232,6 +237,8 @@ class Upload(MemoizationLeaf):
# Mark the Upload component as used in the app. # Mark the Upload component as used in the app.
cls.is_used = True cls.is_used = True
props.setdefault("multiple", True)
# Apply the default classname # Apply the default classname
given_class_name = props.pop("class_name", []) given_class_name = props.pop("class_name", [])
if isinstance(given_class_name, str): if isinstance(given_class_name, str):
@ -243,17 +250,6 @@ class Upload(MemoizationLeaf):
upload_props = { upload_props = {
key: value for key, value in props.items() if key in supported_props key: value for key, value in props.items() if key in supported_props
} }
# The file input to use.
upload = Input.create(type="file")
upload.special_props = [Var(_js_expr="{...getInputProps()}", _var_type=None)]
# The dropzone to use.
zone = Box.create(
upload,
*children,
**{k: v for k, v in props.items() if k not in supported_props},
)
zone.special_props = [Var(_js_expr="{...getRootProps()}", _var_type=None)]
# Create the component. # Create the component.
upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID) upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID)
@ -275,9 +271,74 @@ class Upload(MemoizationLeaf):
), ),
) )
upload_props["on_drop"] = on_drop upload_props["on_drop"] = on_drop
input_props_unique_name = get_unique_variable_name()
root_props_unique_name = get_unique_variable_name()
event_var, callback_str = StatefulComponent._get_memoized_event_triggers(
Box.create(on_click=upload_props["on_drop"]) # type: ignore
)["on_click"]
upload_props["on_drop"] = event_var
upload_props = {
format.to_camel_case(key): value for key, value in upload_props.items()
}
use_dropzone_arguements = {
"onDrop": event_var,
**upload_props,
}
left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} "
right_side = f"useDropzone({str(Var.create(use_dropzone_arguements))})"
var_data = VarData.merge(
VarData(
imports=Imports.EVENTS,
hooks={
"const [addEvents, connectError] = useContext(EventLoopContext);": None
},
),
event_var._get_all_var_data(),
VarData(
hooks={
callback_str: None,
f"{left_side} = {right_side};": None,
},
imports={
"react-dropzone": "useDropzone",
**Imports.EVENTS,
},
),
)
# The file input to use.
upload = Input.create(type="file")
upload.special_props = [
Var(
_js_expr=f"{{...{input_props_unique_name}()}}",
_var_type=None,
_var_data=var_data,
)
]
# The dropzone to use.
zone = Box.create(
upload,
*children,
**{k: v for k, v in props.items() if k not in supported_props},
)
zone.special_props = [
Var(
_js_expr=f"{{...{root_props_unique_name}()}}",
_var_type=None,
_var_data=var_data,
)
]
return super().create( return super().create(
zone, zone,
**upload_props,
) )
@classmethod @classmethod
@ -295,11 +356,6 @@ class Upload(MemoizationLeaf):
return (arg_value[0], placeholder) return (arg_value[0], placeholder)
return arg_value return arg_value
def _render(self):
out = super()._render()
out.args = ("getRootProps", "getInputProps")
return out
@staticmethod @staticmethod
def _get_app_wrap_components() -> dict[tuple[int, str], Component]: def _get_app_wrap_components() -> dict[tuple[int, str], Component]:
return { return {

View File

@ -6,7 +6,11 @@
from pathlib import Path from pathlib import Path
from typing import Any, ClassVar, Dict, List, Optional, Union, overload from typing import Any, ClassVar, Dict, List, Optional, Union, overload
from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf from reflex.components.component import (
Component,
ComponentNamespace,
MemoizationLeaf,
)
from reflex.constants import Dirs from reflex.constants import Dirs
from reflex.event import ( from reflex.event import (
CallableEventSpec, CallableEventSpec,
@ -142,6 +146,7 @@ class Upload(MemoizationLeaf):
no_click: Whether to disable click to upload. no_click: Whether to disable click to upload.
no_drag: Whether to disable drag and drop. no_drag: Whether to disable drag and drop.
no_keyboard: Whether to disable using the space/enter keys to upload. no_keyboard: Whether to disable using the space/enter keys to upload.
on_drop: Fired when files are dropped.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -207,6 +212,7 @@ class StyledUpload(Upload):
no_click: Whether to disable click to upload. no_click: Whether to disable click to upload.
no_drag: Whether to disable drag and drop. no_drag: Whether to disable drag and drop.
no_keyboard: Whether to disable using the space/enter keys to upload. no_keyboard: Whether to disable using the space/enter keys to upload.
on_drop: Fired when files are dropped.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -272,6 +278,7 @@ class UploadNamespace(ComponentNamespace):
no_click: Whether to disable click to upload. no_click: Whether to disable click to upload.
no_drag: Whether to disable drag and drop. no_drag: Whether to disable drag and drop.
no_keyboard: Whether to disable using the space/enter keys to upload. no_keyboard: Whether to disable using the space/enter keys to upload.
on_drop: Fired when files are dropped.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -391,7 +391,7 @@ class CodeBlock(Component):
theme: Var[Union[Theme, str]] = Theme.one_light theme: Var[Union[Theme, str]] = Theme.one_light
# The language to use. # The language to use.
language: Var[LiteralCodeLanguage] = "python" # type: ignore language: Var[LiteralCodeLanguage] = Var.create("python")
# The code to display. # The code to display.
code: Var[str] code: Var[str]
@ -411,6 +411,12 @@ class CodeBlock(Component):
# Props passed down to the code tag. # Props passed down to the code tag.
code_tag_props: Var[Dict[str, str]] code_tag_props: Var[Dict[str, str]]
# Whether a copy button should appear.
can_copy: Optional[bool] = False
# A custom copy button to override the default one.
copy_button: Optional[Union[bool, Component]] = None
def add_imports(self) -> ImportDict: def add_imports(self) -> ImportDict:
"""Add imports for the CodeBlock component. """Add imports for the CodeBlock component.
@ -448,16 +454,12 @@ class CodeBlock(Component):
def create( def create(
cls, cls,
*children, *children,
can_copy: Optional[bool] = False,
copy_button: Optional[Union[bool, Component]] = None,
**props, **props,
): ):
"""Create a text component. """Create a text component.
Args: Args:
*children: The children of the component. *children: The children of the component.
can_copy: Whether a copy button should appears.
copy_button: A custom copy button to override the default one.
**props: The props to pass to the component. **props: The props to pass to the component.
Returns: Returns:
@ -465,6 +467,8 @@ class CodeBlock(Component):
""" """
# This component handles style in a special prop. # This component handles style in a special prop.
custom_style = props.pop("custom_style", {}) custom_style = props.pop("custom_style", {})
can_copy = props.pop("can_copy", False)
copy_button = props.pop("copy_button", None)
if "theme" not in props: if "theme" not in props:
# Default color scheme responds to global color mode. # Default color scheme responds to global color mode.
@ -536,6 +540,9 @@ class CodeBlock(Component):
return out return out
def _exclude_props(self) -> list[str]:
return ["can_copy", "copy_button"]
class CodeblockNamespace(ComponentNamespace): class CodeblockNamespace(ComponentNamespace):
"""Namespace for the CodeBlock component.""" """Namespace for the CodeBlock component."""

View File

@ -356,8 +356,6 @@ class CodeBlock(Component):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
can_copy: Optional[bool] = False,
copy_button: Optional[Union[Component, bool]] = None,
theme: Optional[Union[Theme, Var[Union[Theme, str]], str]] = None, theme: Optional[Union[Theme, Var[Union[Theme, str]], str]] = None,
language: Optional[ language: Optional[
Union[ Union[
@ -933,6 +931,8 @@ class CodeBlock(Component):
wrap_long_lines: Optional[Union[Var[bool], bool]] = None, wrap_long_lines: Optional[Union[Var[bool], bool]] = None,
custom_style: Optional[Dict[str, Union[str, Var, Color]]] = None, custom_style: Optional[Dict[str, Union[str, Var, Color]]] = None,
code_tag_props: Optional[Union[Dict[str, str], Var[Dict[str, str]]]] = None, code_tag_props: Optional[Union[Dict[str, str], Var[Dict[str, str]]]] = None,
can_copy: Optional[bool] = None,
copy_button: Optional[Union[Component, bool]] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
id: Optional[Any] = None, id: Optional[Any] = None,
@ -960,8 +960,6 @@ class CodeBlock(Component):
Args: Args:
*children: The children of the component. *children: The children of the component.
can_copy: Whether a copy button should appears.
copy_button: A custom copy button to override the default one.
theme: The theme to use ("light" or "dark"). theme: The theme to use ("light" or "dark").
language: The language to use. language: The language to use.
code: The code to display. code: The code to display.
@ -970,6 +968,8 @@ class CodeBlock(Component):
wrap_long_lines: Whether to wrap long lines. wrap_long_lines: Whether to wrap long lines.
custom_style: A custom style for the code block. custom_style: A custom style for the code block.
code_tag_props: Props passed down to the code tag. code_tag_props: Props passed down to the code tag.
can_copy: Whether a copy button should appear.
copy_button: A custom copy button to override the default one.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -991,8 +991,6 @@ class CodeblockNamespace(ComponentNamespace):
@staticmethod @staticmethod
def __call__( def __call__(
*children, *children,
can_copy: Optional[bool] = False,
copy_button: Optional[Union[Component, bool]] = None,
theme: Optional[Union[Theme, Var[Union[Theme, str]], str]] = None, theme: Optional[Union[Theme, Var[Union[Theme, str]], str]] = None,
language: Optional[ language: Optional[
Union[ Union[
@ -1568,6 +1566,8 @@ class CodeblockNamespace(ComponentNamespace):
wrap_long_lines: Optional[Union[Var[bool], bool]] = None, wrap_long_lines: Optional[Union[Var[bool], bool]] = None,
custom_style: Optional[Dict[str, Union[str, Var, Color]]] = None, custom_style: Optional[Dict[str, Union[str, Var, Color]]] = None,
code_tag_props: Optional[Union[Dict[str, str], Var[Dict[str, str]]]] = None, code_tag_props: Optional[Union[Dict[str, str], Var[Dict[str, str]]]] = None,
can_copy: Optional[bool] = None,
copy_button: Optional[Union[Component, bool]] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
id: Optional[Any] = None, id: Optional[Any] = None,
@ -1595,8 +1595,6 @@ class CodeblockNamespace(ComponentNamespace):
Args: Args:
*children: The children of the component. *children: The children of the component.
can_copy: Whether a copy button should appears.
copy_button: A custom copy button to override the default one.
theme: The theme to use ("light" or "dark"). theme: The theme to use ("light" or "dark").
language: The language to use. language: The language to use.
code: The code to display. code: The code to display.
@ -1605,6 +1603,8 @@ class CodeblockNamespace(ComponentNamespace):
wrap_long_lines: Whether to wrap long lines. wrap_long_lines: Whether to wrap long lines.
custom_style: A custom style for the code block. custom_style: A custom style for the code block.
code_tag_props: Props passed down to the code tag. code_tag_props: Props passed down to the code tag.
can_copy: Whether a copy button should appear.
copy_button: A custom copy button to override the default one.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -206,7 +206,7 @@ class DataEditor(NoSSRComponent):
get_cell_content: Var[str] get_cell_content: Var[str]
# Allow selection for copying. # Allow selection for copying.
get_cell_for_selection: Var[bool] get_cells_for_selection: Var[bool]
# Allow paste. # Allow paste.
on_paste: Var[bool] on_paste: Var[bool]
@ -424,7 +424,7 @@ class DataEditor(NoSSRComponent):
props["theme"] = DataEditorTheme(**theme) props["theme"] = DataEditorTheme(**theme)
# Allow by default to select a region of cells in the grid. # Allow by default to select a region of cells in the grid.
props.setdefault("get_cell_for_selection", True) props.setdefault("get_cells_for_selection", True)
# Disable on_paste by default if not provided. # Disable on_paste by default if not provided.
props.setdefault("on_paste", False) props.setdefault("on_paste", False)

View File

@ -140,7 +140,7 @@ class DataEditor(NoSSRComponent):
] = None, ] = None,
data: Optional[Union[List[List[Any]], Var[List[List[Any]]]]] = None, data: Optional[Union[List[List[Any]], Var[List[List[Any]]]]] = None,
get_cell_content: Optional[Union[Var[str], str]] = None, get_cell_content: Optional[Union[Var[str], str]] = None,
get_cell_for_selection: Optional[Union[Var[bool], bool]] = None, get_cells_for_selection: Optional[Union[Var[bool], bool]] = None,
on_paste: Optional[Union[Var[bool], bool]] = None, on_paste: Optional[Union[Var[bool], bool]] = None,
draw_focus_ring: Optional[Union[Var[bool], bool]] = None, draw_focus_ring: Optional[Union[Var[bool], bool]] = None,
fixed_shadow_x: Optional[Union[Var[bool], bool]] = None, fixed_shadow_x: Optional[Union[Var[bool], bool]] = None,
@ -228,7 +228,7 @@ class DataEditor(NoSSRComponent):
columns: Headers of the columns for the data grid. columns: Headers of the columns for the data grid.
data: The data. data: The data.
get_cell_content: The name of the callback used to find the data to display. get_cell_content: The name of the callback used to find the data to display.
get_cell_for_selection: Allow selection for copying. get_cells_for_selection: Allow selection for copying.
on_paste: Allow paste. on_paste: Allow paste.
draw_focus_ring: Controls the drawing of the focus ring. draw_focus_ring: Controls the drawing of the focus ring.
fixed_shadow_x: Enables or disables the overlay shadow when scrolling horizontally. fixed_shadow_x: Enables or disables the overlay shadow when scrolling horizontally.
@ -253,6 +253,22 @@ class DataEditor(NoSSRComponent):
scroll_offset_x: Initial scroll offset on the horizontal axis. scroll_offset_x: Initial scroll offset on the horizontal axis.
scroll_offset_y: Initial scroll offset on the vertical axis. scroll_offset_y: Initial scroll offset on the vertical axis.
theme: global theme theme: global theme
on_cell_activated: Fired when a cell is activated.
on_cell_clicked: Fired when a cell is clicked.
on_cell_context_menu: Fired when a cell is right-clicked.
on_cell_edited: Fired when a cell is edited.
on_group_header_clicked: Fired when a group header is clicked.
on_group_header_context_menu: Fired when a group header is right-clicked.
on_group_header_renamed: Fired when a group header is renamed.
on_header_clicked: Fired when a header is clicked.
on_header_context_menu: Fired when a header is right-clicked.
on_header_menu_click: Fired when a header menu item is clicked.
on_item_hovered: Fired when an item is hovered.
on_delete: Fired when a selection is deleted.
on_finished_editing: Fired when editing is finished.
on_row_appended: Fired when a row is appended.
on_selection_cleared: Fired when the selection is cleared.
on_column_resize: Fired when a column is resized.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -12,6 +12,7 @@ from reflex.components.core.colors import color
from reflex.components.core.cond import color_mode_cond from reflex.components.core.cond import color_mode_cond
from reflex.components.el.elements.forms import Button from reflex.components.el.elements.forms import Button
from reflex.components.lucide.icon import Icon from reflex.components.lucide.icon import Icon
from reflex.components.props import NoExtrasAllowedProps
from reflex.components.radix.themes.layout.box import Box from reflex.components.radix.themes.layout.box import Box
from reflex.event import call_script, set_clipboard from reflex.event import call_script, set_clipboard
from reflex.style import Style from reflex.style import Style
@ -33,8 +34,8 @@ def copy_script() -> Any:
f""" f"""
// Event listener for the parent click // Event listener for the parent click
document.addEventListener('click', function(event) {{ document.addEventListener('click', function(event) {{
// Find the closest div (parent element) // Find the closest button (parent element)
const parent = event.target.closest('div'); const parent = event.target.closest('button');
// If the parent is found // If the parent is found
if (parent) {{ if (parent) {{
// Find the SVG element within the parent // Find the SVG element within the parent
@ -253,6 +254,7 @@ LiteralCodeLanguage = Literal[
"pascal", "pascal",
"perl", "perl",
"php", "php",
"plain",
"plsql", "plsql",
"po", "po",
"postcss", "postcss",
@ -369,10 +371,11 @@ LiteralCodeTheme = Literal[
"nord", "nord",
"one-dark-pro", "one-dark-pro",
"one-light", "one-light",
"plain",
"plastic", "plastic",
"poimandres", "poimandres",
"red", "red",
# rose-pine themes dont work with the current version of shikijs transformers
# https://github.com/shikijs/shiki/issues/730
"rose-pine", "rose-pine",
"rose-pine-dawn", "rose-pine-dawn",
"rose-pine-moon", "rose-pine-moon",
@ -390,6 +393,23 @@ LiteralCodeTheme = Literal[
] ]
class Position(NoExtrasAllowedProps):
"""Position of the decoration."""
line: int
character: int
class ShikiDecorations(NoExtrasAllowedProps):
"""Decorations for the code block."""
start: Union[int, Position]
end: Union[int, Position]
tag_name: str = "span"
properties: dict[str, Any] = {}
always_wrap: bool = False
class ShikiBaseTransformers(Base): class ShikiBaseTransformers(Base):
"""Base for creating transformers.""" """Base for creating transformers."""
@ -537,6 +557,9 @@ class ShikiCodeBlock(Component):
[] []
) )
# The decorations to use for the syntax highlighter.
decorations: Var[list[ShikiDecorations]] = Var.create([])
@classmethod @classmethod
def create( def create(
cls, cls,
@ -555,6 +578,7 @@ class ShikiCodeBlock(Component):
# Separate props for the code block and the wrapper # Separate props for the code block and the wrapper
code_block_props = {} code_block_props = {}
code_wrapper_props = {} code_wrapper_props = {}
decorations = props.pop("decorations", [])
class_props = cls.get_props() class_props = cls.get_props()
@ -564,6 +588,15 @@ class ShikiCodeBlock(Component):
value value
) )
# cast decorations into ShikiDecorations.
decorations = [
ShikiDecorations(**decoration)
if not isinstance(decoration, ShikiDecorations)
else decoration
for decoration in decorations
]
code_block_props["decorations"] = decorations
code_block_props["code"] = children[0] code_block_props["code"] = children[0]
code_block = super().create(**code_block_props) code_block = super().create(**code_block_props)
@ -676,10 +709,10 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
show_line_numbers: Var[bool] show_line_numbers: Var[bool]
# Whether a copy button should appear. # Whether a copy button should appear.
can_copy: Var[bool] = Var.create(False) can_copy: bool = False
# copy_button: A custom copy button to override the default one. # copy_button: A custom copy button to override the default one.
copy_button: Var[Optional[Union[Component, bool]]] = Var.create(None) copy_button: Optional[Union[Component, bool]] = None
@classmethod @classmethod
def create( def create(

View File

@ -7,6 +7,7 @@ from typing import Any, Dict, Literal, Optional, Union, overload
from reflex.base import Base from reflex.base import Base
from reflex.components.component import Component, ComponentNamespace from reflex.components.component import Component, ComponentNamespace
from reflex.components.props import NoExtrasAllowedProps
from reflex.event import EventType from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars.base import Var from reflex.vars.base import Var
@ -192,6 +193,7 @@ LiteralCodeLanguage = Literal[
"pascal", "pascal",
"perl", "perl",
"php", "php",
"plain",
"plsql", "plsql",
"po", "po",
"postcss", "postcss",
@ -308,7 +310,6 @@ LiteralCodeTheme = Literal[
"nord", "nord",
"one-dark-pro", "one-dark-pro",
"one-light", "one-light",
"plain",
"plastic", "plastic",
"poimandres", "poimandres",
"red", "red",
@ -328,6 +329,17 @@ LiteralCodeTheme = Literal[
"vitesse-light", "vitesse-light",
] ]
class Position(NoExtrasAllowedProps):
line: int
character: int
class ShikiDecorations(NoExtrasAllowedProps):
start: Union[int, Position]
end: Union[int, Position]
tag_name: str
properties: dict[str, Any]
always_wrap: bool
class ShikiBaseTransformers(Base): class ShikiBaseTransformers(Base):
library: str library: str
fns: list[FunctionStringVar] fns: list[FunctionStringVar]
@ -479,6 +491,7 @@ class ShikiCodeBlock(Component):
"pascal", "pascal",
"perl", "perl",
"php", "php",
"plain",
"plsql", "plsql",
"po", "po",
"postcss", "postcss",
@ -694,6 +707,7 @@ class ShikiCodeBlock(Component):
"pascal", "pascal",
"perl", "perl",
"php", "php",
"plain",
"plsql", "plsql",
"po", "po",
"postcss", "postcss",
@ -815,7 +829,6 @@ class ShikiCodeBlock(Component):
"nord", "nord",
"one-dark-pro", "one-dark-pro",
"one-light", "one-light",
"plain",
"plastic", "plastic",
"poimandres", "poimandres",
"red", "red",
@ -870,7 +883,6 @@ class ShikiCodeBlock(Component):
"nord", "nord",
"one-dark-pro", "one-dark-pro",
"one-light", "one-light",
"plain",
"plastic", "plastic",
"poimandres", "poimandres",
"red", "red",
@ -906,6 +918,9 @@ class ShikiCodeBlock(Component):
list[Union[ShikiBaseTransformers, dict[str, Any]]], list[Union[ShikiBaseTransformers, dict[str, Any]]],
] ]
] = None, ] = None,
decorations: Optional[
Union[Var[list[ShikiDecorations]], list[ShikiDecorations]]
] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
id: Optional[Any] = None, id: Optional[Any] = None,
@ -938,6 +953,7 @@ class ShikiCodeBlock(Component):
themes: The set of themes to use for different modes. themes: The set of themes to use for different modes.
code: The code to display. code: The code to display.
transformers: The transformers to use for the syntax highlighter. transformers: The transformers to use for the syntax highlighter.
decorations: The decorations to use for the syntax highlighter.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -965,10 +981,8 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
*children, *children,
use_transformers: Optional[Union[Var[bool], bool]] = None, use_transformers: Optional[Union[Var[bool], bool]] = None,
show_line_numbers: Optional[Union[Var[bool], bool]] = None, show_line_numbers: Optional[Union[Var[bool], bool]] = None,
can_copy: Optional[Union[Var[bool], bool]] = None, can_copy: Optional[bool] = None,
copy_button: Optional[ copy_button: Optional[Union[Component, bool]] = None,
Union[Component, Var[Optional[Union[Component, bool]]], bool]
] = None,
language: Optional[ language: Optional[
Union[ Union[
Literal[ Literal[
@ -1104,6 +1118,7 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
"pascal", "pascal",
"perl", "perl",
"php", "php",
"plain",
"plsql", "plsql",
"po", "po",
"postcss", "postcss",
@ -1319,6 +1334,7 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
"pascal", "pascal",
"perl", "perl",
"php", "php",
"plain",
"plsql", "plsql",
"po", "po",
"postcss", "postcss",
@ -1440,7 +1456,6 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
"nord", "nord",
"one-dark-pro", "one-dark-pro",
"one-light", "one-light",
"plain",
"plastic", "plastic",
"poimandres", "poimandres",
"red", "red",
@ -1495,7 +1510,6 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
"nord", "nord",
"one-dark-pro", "one-dark-pro",
"one-light", "one-light",
"plain",
"plastic", "plastic",
"poimandres", "poimandres",
"red", "red",
@ -1531,6 +1545,9 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
list[Union[ShikiBaseTransformers, dict[str, Any]]], list[Union[ShikiBaseTransformers, dict[str, Any]]],
] ]
] = None, ] = None,
decorations: Optional[
Union[Var[list[ShikiDecorations]], list[ShikiDecorations]]
] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
id: Optional[Any] = None, id: Optional[Any] = None,
@ -1567,6 +1584,7 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
themes: The set of themes to use for different modes. themes: The set of themes to use for different modes.
code: The code to display. code: The code to display.
transformers: The transformers to use for the syntax highlighter. transformers: The transformers to use for the syntax highlighter.
decorations: The decorations to use for the syntax highlighter.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -1593,10 +1611,8 @@ class CodeblockNamespace(ComponentNamespace):
*children, *children,
use_transformers: Optional[Union[Var[bool], bool]] = None, use_transformers: Optional[Union[Var[bool], bool]] = None,
show_line_numbers: Optional[Union[Var[bool], bool]] = None, show_line_numbers: Optional[Union[Var[bool], bool]] = None,
can_copy: Optional[Union[Var[bool], bool]] = None, can_copy: Optional[bool] = None,
copy_button: Optional[ copy_button: Optional[Union[Component, bool]] = None,
Union[Component, Var[Optional[Union[Component, bool]]], bool]
] = None,
language: Optional[ language: Optional[
Union[ Union[
Literal[ Literal[
@ -1732,6 +1748,7 @@ class CodeblockNamespace(ComponentNamespace):
"pascal", "pascal",
"perl", "perl",
"php", "php",
"plain",
"plsql", "plsql",
"po", "po",
"postcss", "postcss",
@ -1947,6 +1964,7 @@ class CodeblockNamespace(ComponentNamespace):
"pascal", "pascal",
"perl", "perl",
"php", "php",
"plain",
"plsql", "plsql",
"po", "po",
"postcss", "postcss",
@ -2068,7 +2086,6 @@ class CodeblockNamespace(ComponentNamespace):
"nord", "nord",
"one-dark-pro", "one-dark-pro",
"one-light", "one-light",
"plain",
"plastic", "plastic",
"poimandres", "poimandres",
"red", "red",
@ -2123,7 +2140,6 @@ class CodeblockNamespace(ComponentNamespace):
"nord", "nord",
"one-dark-pro", "one-dark-pro",
"one-light", "one-light",
"plain",
"plastic", "plastic",
"poimandres", "poimandres",
"red", "red",
@ -2159,6 +2175,9 @@ class CodeblockNamespace(ComponentNamespace):
list[Union[ShikiBaseTransformers, dict[str, Any]]], list[Union[ShikiBaseTransformers, dict[str, Any]]],
] ]
] = None, ] = None,
decorations: Optional[
Union[Var[list[ShikiDecorations]], list[ShikiDecorations]]
] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
id: Optional[Any] = None, id: Optional[Any] = None,
@ -2195,6 +2214,7 @@ class CodeblockNamespace(ComponentNamespace):
themes: The set of themes to use for different modes. themes: The set of themes to use for different modes.
code: The code to display. code: The code to display.
transformers: The transformers to use for the syntax highlighter. transformers: The transformers to use for the syntax highlighter.
decorations: The decorations to use for the syntax highlighter.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -63,6 +63,9 @@ def load_dynamic_serializer():
""" """
# Causes a circular import, so we import here. # Causes a circular import, so we import here.
from reflex.compiler import templates, utils from reflex.compiler import templates, utils
from reflex.components.base.bare import Bare
component = Bare.create(Var.create(component))
rendered_components = {} rendered_components = {}
# Include dynamic imports in the shared component. # Include dynamic imports in the shared component.
@ -127,14 +130,15 @@ def load_dynamic_serializer():
module_code_lines[ix] = line.replace( module_code_lines[ix] = line.replace(
"export function", "export default function", 1 "export function", "export default function", 1
) )
line_stripped = line.strip()
if line_stripped.startswith("{") and line_stripped.endswith("}"):
module_code_lines[ix] = line_stripped[1:-1]
module_code_lines.insert(0, "const React = window.__reflex.react;") module_code_lines.insert(0, "const React = window.__reflex.react;")
return "\n".join( return "\n".join(
[ [
"//__reflex_evaluate", "//__reflex_evaluate",
"/** @jsx jsx */",
"const { jsx } = window.__reflex['@emotion/react']",
*module_code_lines, *module_code_lines,
] ]
) )

View File

@ -111,6 +111,15 @@ def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]:
return (FORM_DATA,) return (FORM_DATA,)
def on_submit_string_event_spec() -> Tuple[Var[Dict[str, str]]]:
"""Event handler spec for the on_submit event.
Returns:
The event handler spec.
"""
return (FORM_DATA,)
class Form(BaseHTML): class Form(BaseHTML):
"""Display the form element.""" """Display the form element."""
@ -150,7 +159,7 @@ class Form(BaseHTML):
handle_submit_unique_name: Var[str] handle_submit_unique_name: Var[str]
# Fired when the form is submitted # Fired when the form is submitted
on_submit: EventHandler[on_submit_event_spec] on_submit: EventHandler[on_submit_event_spec, on_submit_string_event_spec]
@classmethod @classmethod
def create(cls, *children, **props): def create(cls, *children, **props):

View File

@ -271,6 +271,7 @@ class Fieldset(Element):
... ...
def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]: ... def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]: ...
def on_submit_string_event_spec() -> Tuple[Var[Dict[str, str]]]: ...
class Form(BaseHTML): class Form(BaseHTML):
@overload @overload
@ -337,7 +338,9 @@ class Form(BaseHTML):
on_mouse_over: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None,
on_mouse_up: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_submit: Optional[EventType[Dict[str, Any]]] = None, on_submit: Optional[
Union[EventType[Dict[str, Any]], EventType[Dict[str, str]]]
] = None,
on_unmount: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None,
**props, **props,
) -> "Form": ) -> "Form":
@ -356,6 +359,7 @@ class Form(BaseHTML):
target: Where to display the response after submitting the form target: Where to display the response after submitting the form
reset_on_submit: If true, the form will be cleared after submit. reset_on_submit: If true, the form will be cleared after submit.
handle_submit_unique_name: The name used to make this form's submit handler function unique. handle_submit_unique_name: The name used to make this form's submit handler function unique.
on_submit: Fired when the form is submitted
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
@ -520,6 +524,11 @@ class Input(BaseHTML):
type: Specifies the type of input type: Specifies the type of input
use_map: Name of the image map used with the input use_map: Name of the image map used with the input
value: Value of the input value: Value of the input
on_change: Fired when the input value changes
on_focus: Fired when the input gains focus
on_blur: Fired when the input loses focus
on_key_down: Fired when a key is pressed down
on_key_up: Fired when a key is released
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
@ -1269,6 +1278,7 @@ class Select(BaseHTML):
name: Name of the select, used when submitting the form name: Name of the select, used when submitting the form
required: Indicates that the select control must have a selected option required: Indicates that the select control must have a selected option
size: Number of visible options in a drop-down list size: Number of visible options in a drop-down list
on_change: Fired when the select value changes
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
@ -1397,6 +1407,11 @@ class Textarea(BaseHTML):
rows: Visible number of lines in the text control rows: Visible number of lines in the text control
value: The controlled value of the textarea, read only unless used with on_change value: The controlled value of the textarea, read only unless used with on_change
wrap: How the text in the textarea is to be wrapped when submitting the form wrap: How the text in the textarea is to be wrapped when submitting the form
on_change: Fired when the input value changes
on_focus: Fired when the input gains focus
on_blur: Fired when the input loses focus
on_key_down: Fired when a key is pressed down
on_key_up: Fired when a key is released
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.

View File

@ -101,6 +101,7 @@ class Moment(NoSSRComponent):
local: Outputs the result in local time. local: Outputs the result in local time.
tz: Display the date in the given timezone. tz: Display the date in the given timezone.
locale: The locale to use when rendering. locale: The locale to use when rendering.
on_change: Fires when the date changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -72,6 +72,8 @@ class Image(NextComponent):
placeholder: A placeholder to use while the image is loading. Possible values are blur, empty, or data:image/.... Defaults to empty. placeholder: A placeholder to use while the image is loading. Possible values are blur, empty, or data:image/.... Defaults to empty.
loading: Allows passing CSS styles to the underlying image element. style: Var[Any] The loading behavior of the image. Defaults to lazy. Can hurt performance, recommended to use `priority` instead. loading: Allows passing CSS styles to the underlying image element. style: Var[Any] The loading behavior of the image. Defaults to lazy. Can hurt performance, recommended to use `priority` instead.
blurDataURL: A Data URL to be used as a placeholder image before the src image successfully loads. Only takes effect when combined with placeholder="blur". blurDataURL: A Data URL to be used as a placeholder image before the src image successfully loads. Only takes effect when combined with placeholder="blur".
on_load: Fires when the image has loaded.
on_error: Fires when the image has an error.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -89,6 +89,26 @@ class Plotly(NoSSRComponent):
template: The template for visual appearance of the graph. template: The template for visual appearance of the graph.
config: The config of the graph. config: The config of the graph.
use_resize_handler: If true, the graph will resize when the window is resized. use_resize_handler: If true, the graph will resize when the window is resized.
on_after_plot: Fired after the plot is redrawn.
on_animated: Fired after the plot was animated.
on_animating_frame: Fired while animating a single frame (does not currently pass data through).
on_animation_interrupted: Fired when an animation is interrupted (to start a new animation for example).
on_autosize: Fired when the plot is responsively sized.
on_before_hover: Fired whenever mouse moves over a plot.
on_button_clicked: Fired when a plotly UI button is clicked.
on_click: Fired when the plot is clicked.
on_deselect: Fired when a selection is cleared (via double click).
on_double_click: Fired when the plot is double clicked.
on_hover: Fired when a plot element is hovered over.
on_relayout: Fired after the plot is layed out (zoom, pan, etc).
on_relayouting: Fired while the plot is being layed out.
on_restyle: Fired after the plot style is changed.
on_redraw: Fired after the plot is redrawn.
on_selected: Fired after selecting plot elements.
on_selecting: Fired while dragging a selection.
on_transitioning: Fired while an animation is occuring.
on_transition_interrupted: Fired when a transition is stopped early.
on_unhover: Fired when a hovered element is no longer hovered.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -2,8 +2,11 @@
from __future__ import annotations from __future__ import annotations
from pydantic import ValidationError
from reflex.base import Base from reflex.base import Base
from reflex.utils import format from reflex.utils import format
from reflex.utils.exceptions import InvalidPropValueError
from reflex.vars.object import LiteralObjectVar from reflex.vars.object import LiteralObjectVar
@ -40,3 +43,34 @@ class PropsBase(Base):
format.to_camel_case(key): value format.to_camel_case(key): value
for key, value in super().dict(*args, **kwargs).items() for key, value in super().dict(*args, **kwargs).items()
} }
class NoExtrasAllowedProps(Base):
"""A class that holds props to be passed or applied to a component with no extra props allowed."""
def __init__(self, component_name=None, **kwargs):
"""Initialize the props.
Args:
component_name: The custom name of the component.
kwargs: Kwargs to initialize the props.
Raises:
InvalidPropValueError: If invalid props are passed on instantiation.
"""
component_name = component_name or type(self).__name__
try:
super().__init__(**kwargs)
except ValidationError as e:
invalid_fields = ", ".join([error["loc"][0] for error in e.errors()]) # type: ignore
supported_props_str = ", ".join(f'"{field}"' for field in self.get_fields())
raise InvalidPropValueError(
f"Invalid prop(s) {invalid_fields} for {component_name!r}. Supported props are {supported_props_str}"
) from None
class Config:
"""Pydantic config."""
arbitrary_types_allowed = True
use_enum_values = True
extra = "forbid"

View File

@ -2,7 +2,7 @@
from __future__ import annotations from __future__ import annotations
from typing import Any, List, Literal, Optional, Tuple, Union from typing import Any, List, Literal, Tuple, Union
from reflex.components.component import Component, ComponentNamespace from reflex.components.component import Component, ComponentNamespace
from reflex.components.core.colors import color from reflex.components.core.colors import color
@ -193,6 +193,11 @@ class AccordionItem(AccordionComponent):
# When true, prevents the user from interacting with the item. # When true, prevents the user from interacting with the item.
disabled: Var[bool] disabled: Var[bool]
# The header of the accordion item.
header: Var[Union[Component, str]]
# The content of the accordion item.
content: Var[Union[Component, str]] = Var.create(None)
_valid_children: List[str] = [ _valid_children: List[str] = [
"AccordionHeader", "AccordionHeader",
"AccordionTrigger", "AccordionTrigger",
@ -205,21 +210,20 @@ class AccordionItem(AccordionComponent):
def create( def create(
cls, cls,
*children, *children,
header: Optional[Component | Var] = None,
content: Optional[Component | Var] = None,
**props, **props,
) -> Component: ) -> Component:
"""Create an accordion item. """Create an accordion item.
Args: Args:
*children: The list of children to use if header and content are not provided. *children: The list of children to use if header and content are not provided.
header: The header of the accordion item.
content: The content of the accordion item.
**props: Additional properties to apply to the accordion item. **props: Additional properties to apply to the accordion item.
Returns: Returns:
The accordion item. The accordion item.
""" """
header = props.pop("header", None)
content = props.pop("content", None)
# The item requires a value to toggle (use a random unique name if not provided). # The item requires a value to toggle (use a random unique name if not provided).
value = props.pop("value", get_uuid_string_var()) value = props.pop("value", get_uuid_string_var())
@ -291,6 +295,9 @@ class AccordionItem(AccordionComponent):
}, },
} }
def _exclude_props(self) -> list[str]:
return ["header", "content"]
class AccordionHeader(AccordionComponent): class AccordionHeader(AccordionComponent):
"""An accordion component.""" """An accordion component."""

View File

@ -280,6 +280,7 @@ class AccordionRoot(AccordionComponent):
duration: The time in milliseconds to animate open and close duration: The time in milliseconds to animate open and close
easing: The easing function to use for the animation. easing: The easing function to use for the animation.
show_dividers: Whether to show divider lines between items. show_dividers: Whether to show divider lines between items.
on_value_change: Fired when the opened the accordions changes.
color_scheme: The color scheme of the component. color_scheme: The color scheme of the component.
variant: The variant of the component. variant: The variant of the component.
as_child: Change the default rendered element for the one passed as a child. as_child: Change the default rendered element for the one passed as a child.
@ -302,10 +303,10 @@ class AccordionItem(AccordionComponent):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
header: Optional[Union[Component, Var]] = None,
content: Optional[Union[Component, Var]] = None,
value: Optional[Union[Var[str], str]] = None, value: Optional[Union[Var[str], str]] = None,
disabled: Optional[Union[Var[bool], bool]] = None, disabled: Optional[Union[Var[bool], bool]] = None,
header: Optional[Union[Component, Var[Union[Component, str]], str]] = None,
content: Optional[Union[Component, Var[Union[Component, str]], str]] = None,
color_scheme: Optional[ color_scheme: Optional[
Union[ Union[
Literal[ Literal[
@ -402,10 +403,10 @@ class AccordionItem(AccordionComponent):
Args: Args:
*children: The list of children to use if header and content are not provided. *children: The list of children to use if header and content are not provided.
header: The header of the accordion item.
content: The content of the accordion item.
value: A unique identifier for the item. value: A unique identifier for the item.
disabled: When true, prevents the user from interacting with the item. disabled: When true, prevents the user from interacting with the item.
header: The header of the accordion item.
content: The content of the accordion item.
color_scheme: The color scheme of the component. color_scheme: The color scheme of the component.
variant: The variant of the component. variant: The variant of the component.
as_child: Change the default rendered element for the one passed as a child. as_child: Change the default rendered element for the one passed as a child.

View File

@ -91,8 +91,8 @@ class DrawerTrigger(DrawerComponent):
"""Create a new DrawerTrigger instance. """Create a new DrawerTrigger instance.
Args: Args:
children: The children of the element. *children: The children of the element.
props: The properties of the element. **props: The properties of the element.
Returns: Returns:
The new DrawerTrigger instance. The new DrawerTrigger instance.

View File

@ -173,8 +173,15 @@ class DrawerTrigger(DrawerComponent):
"""Create a new DrawerTrigger instance. """Create a new DrawerTrigger instance.
Args: Args:
children: The children of the element. *children: The children of the element.
props: The properties of the element. as_child: Change the default rendered element for the one passed as a child.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded
custom_attrs: custom attribute
**props: The properties of the element.
Returns: Returns:
The new DrawerTrigger instance. The new DrawerTrigger instance.
@ -367,8 +374,15 @@ class DrawerClose(DrawerTrigger):
"""Create a new DrawerTrigger instance. """Create a new DrawerTrigger instance.
Args: Args:
children: The children of the element. *children: The children of the element.
props: The properties of the element. as_child: Change the default rendered element for the one passed as a child.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded
custom_attrs: custom attribute
**props: The properties of the element.
Returns: Returns:
The new DrawerTrigger instance. The new DrawerTrigger instance.

View File

@ -129,7 +129,9 @@ class FormRoot(FormComponent, HTMLForm):
on_mouse_over: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None,
on_mouse_up: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_submit: Optional[EventType[Dict[str, Any]]] = None, on_submit: Optional[
Union[EventType[Dict[str, Any]], EventType[Dict[str, str]]]
] = None,
on_unmount: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None,
**props, **props,
) -> "FormRoot": ) -> "FormRoot":
@ -137,6 +139,7 @@ class FormRoot(FormComponent, HTMLForm):
Args: Args:
*children: The children of the form. *children: The children of the form.
on_clear_server_errors: Fired when the errors are cleared.
as_child: Change the default rendered element for the one passed as a child. as_child: Change the default rendered element for the one passed as a child.
accept: MIME types the server accepts for file upload accept: MIME types the server accepts for file upload
accept_charset: Character encodings to be used for form submission accept_charset: Character encodings to be used for form submission
@ -149,6 +152,7 @@ class FormRoot(FormComponent, HTMLForm):
target: Where to display the response after submitting the form target: Where to display the response after submitting the form
reset_on_submit: If true, the form will be cleared after submit. reset_on_submit: If true, the form will be cleared after submit.
handle_submit_unique_name: The name used to make this form's submit handler function unique. handle_submit_unique_name: The name used to make this form's submit handler function unique.
on_submit: Fired when the form is submitted
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
@ -594,7 +598,9 @@ class Form(FormRoot):
on_mouse_over: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None,
on_mouse_up: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_submit: Optional[EventType[Dict[str, Any]]] = None, on_submit: Optional[
Union[EventType[Dict[str, Any]], EventType[Dict[str, str]]]
] = None,
on_unmount: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None,
**props, **props,
) -> "Form": ) -> "Form":
@ -602,6 +608,7 @@ class Form(FormRoot):
Args: Args:
*children: The children of the form. *children: The children of the form.
on_clear_server_errors: Fired when the errors are cleared.
as_child: Change the default rendered element for the one passed as a child. as_child: Change the default rendered element for the one passed as a child.
accept: MIME types the server accepts for file upload accept: MIME types the server accepts for file upload
accept_charset: Character encodings to be used for form submission accept_charset: Character encodings to be used for form submission
@ -614,6 +621,7 @@ class Form(FormRoot):
target: Where to display the response after submitting the form target: Where to display the response after submitting the form
reset_on_submit: If true, the form will be cleared after submit. reset_on_submit: If true, the form will be cleared after submit.
handle_submit_unique_name: The name used to make this form's submit handler function unique. handle_submit_unique_name: The name used to make this form's submit handler function unique.
on_submit: Fired when the form is submitted
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
@ -716,7 +724,9 @@ class FormNamespace(ComponentNamespace):
on_mouse_over: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None,
on_mouse_up: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_submit: Optional[EventType[Dict[str, Any]]] = None, on_submit: Optional[
Union[EventType[Dict[str, Any]], EventType[Dict[str, str]]]
] = None,
on_unmount: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None,
**props, **props,
) -> "Form": ) -> "Form":
@ -724,6 +734,7 @@ class FormNamespace(ComponentNamespace):
Args: Args:
*children: The children of the form. *children: The children of the form.
on_clear_server_errors: Fired when the errors are cleared.
as_child: Change the default rendered element for the one passed as a child. as_child: Change the default rendered element for the one passed as a child.
accept: MIME types the server accepts for file upload accept: MIME types the server accepts for file upload
accept_charset: Character encodings to be used for form submission accept_charset: Character encodings to be used for form submission
@ -736,6 +747,7 @@ class FormNamespace(ComponentNamespace):
target: Where to display the response after submitting the form target: Where to display the response after submitting the form
reset_on_submit: If true, the form will be cleared after submit. reset_on_submit: If true, the form will be cleared after submit.
handle_submit_unique_name: The name used to make this form's submit handler function unique. handle_submit_unique_name: The name used to make this form's submit handler function unique.
on_submit: Fired when the form is submitted
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.

View File

@ -117,6 +117,8 @@ class SliderRoot(SliderComponent):
Args: Args:
*children: The children of the component. *children: The children of the component.
on_value_change: Fired when the value of a thumb changes.
on_value_commit: Fired when a thumb is released.
as_child: Change the default rendered element for the one passed as a child. as_child: Change the default rendered element for the one passed as a child.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.

View File

@ -17,7 +17,7 @@ rx.text(
from __future__ import annotations from __future__ import annotations
from typing import Dict, List, Literal, get_args from typing import Dict, List, Literal, Optional, Union, get_args
from reflex.components.component import BaseComponent from reflex.components.component import BaseComponent
from reflex.components.core.cond import Cond, color_mode_cond, cond from reflex.components.core.cond import Cond, color_mode_cond, cond
@ -96,26 +96,31 @@ def _set_static_default(props, position, prop, default):
class ColorModeIconButton(IconButton): class ColorModeIconButton(IconButton):
"""Icon Button for toggling light / dark mode via toggle_color_mode.""" """Icon Button for toggling light / dark mode via toggle_color_mode."""
# The position of the icon button. Follow document flow if None.
position: Optional[Union[LiteralPosition, Var[LiteralPosition]]] = None
# Allow picking the "system" value for the color mode.
allow_system: bool = False
@classmethod @classmethod
def create( def create(
cls, cls,
position: LiteralPosition | None = None,
allow_system: bool = False,
**props, **props,
): ):
"""Create a icon button component that calls toggle_color_mode on click. """Create an icon button component that calls toggle_color_mode on click.
Args: Args:
position: The position of the icon button. Follow document flow if None.
allow_system: Allow picking the "system" value for the color mode.
**props: The props to pass to the component. **props: The props to pass to the component.
Returns: Returns:
The button component. The button component.
""" """
position = props.pop("position", None)
allow_system = props.pop("allow_system", False)
# position is used to set nice defaults for positioning the icon button # position is used to set nice defaults for positioning the icon button
if isinstance(position, Var): if isinstance(position, Var):
_set_var_default(props, position, "position", "fixed", position) _set_var_default(props, position, "position", "fixed", position) # type: ignore
_set_var_default(props, position, "bottom", "2rem") _set_var_default(props, position, "bottom", "2rem")
_set_var_default(props, position, "top", "2rem") _set_var_default(props, position, "top", "2rem")
_set_var_default(props, position, "left", "2rem") _set_var_default(props, position, "left", "2rem")
@ -155,12 +160,15 @@ class ColorModeIconButton(IconButton):
color_mode_item("system"), color_mode_item("system"),
), ),
) )
return super().create( return IconButton.create(
ColorModeIcon.create(), ColorModeIcon.create(),
on_click=toggle_color_mode, on_click=toggle_color_mode,
**props, **props,
) )
def _exclude_props(self) -> list[str]:
return ["position", "allow_system"]
class ColorModeSwitch(Switch): class ColorModeSwitch(Switch):
"""Switch for toggling light / dark mode via toggle_color_mode.""" """Switch for toggling light / dark mode via toggle_color_mode."""

View File

@ -75,6 +75,18 @@ class ColorModeIconButton(IconButton):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
position: Optional[
Union[
Literal["bottom-left", "bottom-right", "top-left", "top-right"],
Union[
Literal["bottom-left", "bottom-right", "top-left", "top-right"],
Var[
Literal["bottom-left", "bottom-right", "top-left", "top-right"]
],
],
]
] = None,
allow_system: Optional[bool] = None,
as_child: Optional[Union[Var[bool], bool]] = None, as_child: Optional[Union[Var[bool], bool]] = None,
size: Optional[ size: Optional[
Union[ Union[
@ -226,7 +238,7 @@ class ColorModeIconButton(IconButton):
on_unmount: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None,
**props, **props,
) -> "ColorModeIconButton": ) -> "ColorModeIconButton":
"""Create a icon button component that calls toggle_color_mode on click. """Create an icon button component that calls toggle_color_mode on click.
Args: Args:
position: The position of the icon button. Follow document flow if None. position: The position of the icon button. Follow document flow if None.
@ -416,6 +428,7 @@ class ColorModeSwitch(Switch):
color_scheme: Override theme color for switch color_scheme: Override theme color for switch
high_contrast: Whether to render the switch with higher contrast color against background high_contrast: Whether to render the switch with higher contrast color against background
radius: Override theme radius for switch: "none" | "small" | "full" radius: Override theme radius for switch: "none" | "small" | "full"
on_change: Props to rename Fired when the value of the switch changes
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -57,6 +57,7 @@ class AlertDialogRoot(RadixThemesComponent):
*children: Child components. *children: Child components.
open: The controlled open state of the dialog. open: The controlled open state of the dialog.
default_open: The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. default_open: The open state of the dialog when it is initially rendered. Use when you do not need to control its open state.
on_open_change: Fired when the open state changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -188,6 +189,9 @@ class AlertDialogContent(elements.Div, RadixThemesComponent):
*children: Child components. *children: Child components.
size: The size of the content. size: The size of the content.
force_mount: Whether to force mount the content on open. force_mount: Whether to force mount the content on open.
on_open_auto_focus: Fired when the dialog is opened.
on_close_auto_focus: Fired when the dialog is closed.
on_escape_key_down: Fired when the escape key is pressed.
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.

View File

@ -151,6 +151,7 @@ class Checkbox(RadixThemesComponent):
required: Whether the checkbox is required required: Whether the checkbox is required
name: The name of the checkbox control when submitting the form. name: The name of the checkbox control when submitting the form.
value: The value of the checkbox control when submitting the form. value: The value of the checkbox control when submitting the form.
on_change: Props to rename Fired when the checkbox is checked or unchecked.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -297,6 +298,7 @@ class HighLevelCheckbox(RadixThemesComponent):
required: Whether the checkbox is required required: Whether the checkbox is required
name: The name of the checkbox control when submitting the form. name: The name of the checkbox control when submitting the form.
value: The value of the checkbox control when submitting the form. value: The value of the checkbox control when submitting the form.
on_change: Props to rename Fired when the checkbox is checked or unchecked.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -441,6 +443,7 @@ class CheckboxNamespace(ComponentNamespace):
required: Whether the checkbox is required required: Whether the checkbox is required
name: The name of the checkbox control when submitting the form. name: The name of the checkbox control when submitting the form.
value: The value of the checkbox control when submitting the form. value: The value of the checkbox control when submitting the form.
on_change: Props to rename Fired when the checkbox is checked or unchecked.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -61,6 +61,7 @@ class ContextMenuRoot(RadixThemesComponent):
*children: Child components. *children: Child components.
modal: The modality of the context menu. When set to true, interaction with outside elements will be disabled and only menu content will be visible to screen readers. modal: The modality of the context menu. When set to true, interaction with outside elements will be disabled and only menu content will be visible to screen readers.
dir: The reading direction of submenus when applicable. If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode. dir: The reading direction of submenus when applicable. If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode.
on_open_change: Fired when the open state changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -284,6 +285,11 @@ class ContextMenuContent(RadixThemesComponent):
collision_padding: The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. collision_padding: The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0.
sticky: The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial". sticky: The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial".
hide_when_detached: Whether to hide the content when the trigger becomes fully occluded. Defaults to False. hide_when_detached: Whether to hide the content when the trigger becomes fully occluded. Defaults to False.
on_close_auto_focus: Fired when the context menu is closed.
on_escape_key_down: Fired when the escape key is pressed.
on_pointer_down_outside: Fired when a pointer down event happens outside the context menu.
on_focus_outside: Fired when focus moves outside the context menu.
on_interact_outside: Fired when interacting outside the context menu.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -473,6 +479,10 @@ class ContextMenuSubContent(RadixThemesComponent):
collision_padding: The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. collision_padding: The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0.
sticky: The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial". sticky: The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial".
hide_when_detached: Whether to hide the content when the trigger becomes fully occluded. Defaults to False. hide_when_detached: Whether to hide the content when the trigger becomes fully occluded. Defaults to False.
on_escape_key_down: Fired when the escape key is pressed.
on_pointer_down_outside: Fired when a pointer down event happens outside the context menu.
on_focus_outside: Fired when focus moves outside the context menu.
on_interact_outside: Fired when interacting outside the context menu.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -55,6 +55,7 @@ class DialogRoot(RadixThemesComponent):
*children: Child components. *children: Child components.
open: The controlled open state of the dialog. open: The controlled open state of the dialog.
default_open: The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. default_open: The open state of the dialog when it is initially rendered. Use when you do not need to control its open state.
on_open_change: Fired when the open state changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -235,6 +236,11 @@ class DialogContent(elements.Div, RadixThemesComponent):
Args: Args:
*children: Child components. *children: Child components.
size: DialogContent size "1" - "4" size: DialogContent size "1" - "4"
on_open_auto_focus: Fired when the dialog is opened.
on_close_auto_focus: Fired when the dialog is closed.
on_escape_key_down: Fired when the escape key is pressed.
on_pointer_down_outside: Fired when the pointer is down outside the dialog.
on_interact_outside: Fired when the pointer interacts outside the dialog.
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
@ -399,6 +405,7 @@ class Dialog(ComponentNamespace):
*children: Child components. *children: Child components.
open: The controlled open state of the dialog. open: The controlled open state of the dialog.
default_open: The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. default_open: The open state of the dialog when it is initially rendered. Use when you do not need to control its open state.
on_open_change: Fired when the open state changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -65,6 +65,7 @@ class DropdownMenuRoot(RadixThemesComponent):
open: The controlled open state of the dropdown menu. Must be used in conjunction with onOpenChange. open: The controlled open state of the dropdown menu. Must be used in conjunction with onOpenChange.
modal: The modality of the dropdown menu. When set to true, interaction with outside elements will be disabled and only menu content will be visible to screen readers. Defaults to True. modal: The modality of the dropdown menu. When set to true, interaction with outside elements will be disabled and only menu content will be visible to screen readers. Defaults to True.
dir: The reading direction of submenus when applicable. If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode. dir: The reading direction of submenus when applicable. If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode.
on_open_change: Fired when the open state changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -278,6 +279,11 @@ class DropdownMenuContent(RadixThemesComponent):
collision_padding: The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. collision_padding: The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0.
sticky: The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial". sticky: The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial".
hide_when_detached: Whether to hide the content when the trigger becomes fully occluded. Defaults to False. hide_when_detached: Whether to hide the content when the trigger becomes fully occluded. Defaults to False.
on_close_auto_focus: Fired when the dialog is closed.
on_escape_key_down: Fired when the escape key is pressed.
on_pointer_down_outside: Fired when the pointer is down outside the dialog.
on_focus_outside: Fired when focus moves outside the dialog.
on_interact_outside: Fired when the pointer interacts outside the dialog.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -375,6 +381,7 @@ class DropdownMenuSub(RadixThemesComponent):
*children: Child components. *children: Child components.
open: The controlled open state of the submenu. Must be used in conjunction with `on_open_change`. open: The controlled open state of the submenu. Must be used in conjunction with `on_open_change`.
default_open: The open state of the submenu when it is initially rendered. Use when you do not need to control its open state. default_open: The open state of the submenu when it is initially rendered. Use when you do not need to control its open state.
on_open_change: Fired when the open state changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -455,6 +462,10 @@ class DropdownMenuSubContent(RadixThemesComponent):
collision_padding: The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. collision_padding: The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0.
sticky: The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial". sticky: The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial".
hide_when_detached: Whether to hide the content when the trigger becomes fully occluded. Defaults to False. hide_when_detached: Whether to hide the content when the trigger becomes fully occluded. Defaults to False.
on_escape_key_down: Fired when the escape key is pressed.
on_pointer_down_outside: Fired when the pointer is down outside the dialog.
on_focus_outside: Fired when focus moves outside the dialog.
on_interact_outside: Fired when the pointer interacts outside the dialog.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -576,6 +587,7 @@ class DropdownMenuItem(RadixThemesComponent):
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False.
disabled: When true, prevents the user from interacting with the item. disabled: When true, prevents the user from interacting with the item.
text_value: Optional text used for typeahead purposes. By default the typeahead behavior will use the .textContent of the item. Use this when the content is complex, or you have non-textual content inside. text_value: Optional text used for typeahead purposes. By default the typeahead behavior will use the .textContent of the item. Use this when the content is complex, or you have non-textual content inside.
on_select: Fired when the item is selected.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -59,6 +59,7 @@ class HoverCardRoot(RadixThemesComponent):
open: The controlled open state of the hover card. Must be used in conjunction with onOpenChange. open: The controlled open state of the hover card. Must be used in conjunction with onOpenChange.
open_delay: The duration from when the mouse enters the trigger until the hover card opens. open_delay: The duration from when the mouse enters the trigger until the hover card opens.
close_delay: The duration from when the mouse leaves the trigger until the hover card closes. close_delay: The duration from when the mouse leaves the trigger until the hover card closes.
on_open_change: Fired when the open state changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -301,6 +302,7 @@ class HoverCard(ComponentNamespace):
open: The controlled open state of the hover card. Must be used in conjunction with onOpenChange. open: The controlled open state of the hover card. Must be used in conjunction with onOpenChange.
open_delay: The duration from when the mouse enters the trigger until the hover card opens. open_delay: The duration from when the mouse enters the trigger until the hover card opens.
close_delay: The duration from when the mouse leaves the trigger until the hover card closes. close_delay: The duration from when the mouse leaves the trigger until the hover card closes.
on_open_change: Fired when the open state changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -57,6 +57,7 @@ class PopoverRoot(RadixThemesComponent):
open: The controlled open state of the popover. open: The controlled open state of the popover.
modal: The modality of the popover. When set to true, interaction with outside elements will be disabled and only popover content will be visible to screen readers. modal: The modality of the popover. When set to true, interaction with outside elements will be disabled and only popover content will be visible to screen readers.
default_open: The open state of the popover when it is initially rendered. Use when you do not need to control its open state. default_open: The open state of the popover when it is initially rendered. Use when you do not need to control its open state.
on_open_change: Fired when the open state changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -224,6 +225,12 @@ class PopoverContent(elements.Div, RadixThemesComponent):
collision_padding: The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. collision_padding: The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0.
sticky: The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial". sticky: The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial".
hide_when_detached: Whether to hide the content when the trigger becomes fully occluded. Defaults to False. hide_when_detached: Whether to hide the content when the trigger becomes fully occluded. Defaults to False.
on_open_auto_focus: Fired when the dialog is opened.
on_close_auto_focus: Fired when the dialog is closed.
on_escape_key_down: Fired when the escape key is pressed.
on_pointer_down_outside: Fired when the pointer is down outside the dialog.
on_focus_outside: Fired when focus moves outside the dialog.
on_interact_outside: Fired when the pointer interacts outside the dialog.
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.

View File

@ -201,6 +201,7 @@ class RadioCardsRoot(RadixThemesComponent):
orientation: The orientation of the component. orientation: The orientation of the component.
dir: The reading direction of the radio group. If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode. dir: The reading direction of the radio group. If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode.
loop: When true, keyboard navigation will loop from last item to first, and vice versa. loop: When true, keyboard navigation will loop from last item to first, and vice versa.
on_value_change: Event handler called when the value changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -146,6 +146,7 @@ class RadioGroupRoot(RadixThemesComponent):
disabled: Whether the radio group is disabled disabled: Whether the radio group is disabled
name: The name of the group. Submitted with its owning form as part of a name/value pair. name: The name of the group. Submitted with its owning form as part of a name/value pair.
required: Whether the radio group is required required: Whether the radio group is required
on_change: Props to rename Fired when the value of the radio group changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -148,6 +148,7 @@ class SegmentedControlRoot(RadixThemesComponent):
radius: The radius of the segmented control: "none" | "small" | "medium" | "large" | "full" radius: The radius of the segmented control: "none" | "small" | "medium" | "large" | "full"
default_value: The default value of the segmented control. default_value: The default value of the segmented control.
value: The current value of the segmented control. value: The current value of the segmented control.
on_change: Handles the `onChange` event for the SegmentedControl component.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -77,6 +77,8 @@ class SelectRoot(RadixThemesComponent):
name: The name of the select control when submitting the form. name: The name of the select control when submitting the form.
disabled: When True, prevents the user from interacting with select. disabled: When True, prevents the user from interacting with select.
required: When True, indicates that the user must select a value before the owning form can be submitted. required: When True, indicates that the user must select a value before the owning form can be submitted.
on_change: Props to rename Fired when the value of the select changes.
on_open_change: Fired when the select is opened or closed.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -351,6 +353,9 @@ class SelectContent(RadixThemesComponent):
side_offset: The distance in pixels from the anchor. Only available when position is set to popper. side_offset: The distance in pixels from the anchor. Only available when position is set to popper.
align: The preferred alignment against the anchor. May change when collisions occur. Only available when position is set to popper. align: The preferred alignment against the anchor. May change when collisions occur. Only available when position is set to popper.
align_offset: The vertical distance in pixels from the anchor. Only available when position is set to popper. align_offset: The vertical distance in pixels from the anchor. Only available when position is set to popper.
on_close_auto_focus: Fired when the select content is closed.
on_escape_key_down: Fired when the escape key is pressed.
on_pointer_down_outside: Fired when a pointer down event happens outside the select content.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -719,6 +724,8 @@ class HighLevelSelect(SelectRoot):
name: The name of the select control when submitting the form. name: The name of the select control when submitting the form.
disabled: When True, prevents the user from interacting with select. disabled: When True, prevents the user from interacting with select.
required: When True, indicates that the user must select a value before the owning form can be submitted. required: When True, indicates that the user must select a value before the owning form can be submitted.
on_change: Props to rename Fired when the value of the select changes.
on_open_change: Fired when the select is opened or closed.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -893,6 +900,8 @@ class Select(ComponentNamespace):
name: The name of the select control when submitting the form. name: The name of the select control when submitting the form.
disabled: When True, prevents the user from interacting with select. disabled: When True, prevents the user from interacting with select.
required: When True, indicates that the user must select a value before the owning form can be submitted. required: When True, indicates that the user must select a value before the owning form can be submitted.
on_change: Props to rename Fired when the value of the select changes.
on_open_change: Fired when the select is opened or closed.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -2,11 +2,11 @@
from __future__ import annotations from __future__ import annotations
from typing import List, Literal, Optional, Tuple, Union from typing import List, Literal, Optional, Union
from reflex.components.component import Component from reflex.components.component import Component
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.event import EventHandler from reflex.event import EventHandler, identity_event
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import ( from ..base import (
@ -14,19 +14,11 @@ from ..base import (
RadixThemesComponent, RadixThemesComponent,
) )
on_value_event_spec = (
def on_value_event_spec( identity_event(list[Union[int, float]]),
value: Var[List[int | float]], identity_event(list[int]),
) -> Tuple[Var[List[int | float]]]: identity_event(list[float]),
"""Event handler spec for the value event. )
Args:
value: The value of the event.
Returns:
The event handler spec.
"""
return (value,) # type: ignore
class Slider(RadixThemesComponent): class Slider(RadixThemesComponent):
@ -61,6 +53,9 @@ class Slider(RadixThemesComponent):
# The name of the slider. Submitted with its owning form as part of a name/value pair. # The name of the slider. Submitted with its owning form as part of a name/value pair.
name: Var[str] name: Var[str]
# The width of the slider.
width: Var[Optional[str]] = Var.create("100%")
# The minimum value of the slider. # The minimum value of the slider.
min: Var[Union[float, int]] min: Var[Union[float, int]]
@ -89,20 +84,19 @@ class Slider(RadixThemesComponent):
def create( def create(
cls, cls,
*children, *children,
width: Optional[str] = "100%",
**props, **props,
) -> Component: ) -> Component:
"""Create a Slider component. """Create a Slider component.
Args: Args:
*children: The children of the component. *children: The children of the component.
width: The width of the slider.
**props: The properties of the component. **props: The properties of the component.
Returns: Returns:
The component. The component.
""" """
default_value = props.pop("default_value", [50]) default_value = props.pop("default_value", [50])
width = props.pop("width", "100%")
if isinstance(default_value, Var): if isinstance(default_value, Var):
if issubclass(default_value._var_type, (int, float)): if issubclass(default_value._var_type, (int, float)):

View File

@ -3,18 +3,20 @@
# ------------------- DO NOT EDIT ---------------------- # ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`! # This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------ # ------------------------------------------------------
from typing import Any, Dict, List, Literal, Optional, Tuple, Union, overload from typing import Any, Dict, List, Literal, Optional, Union, overload
from reflex.components.core.breakpoints import Breakpoints from reflex.components.core.breakpoints import Breakpoints
from reflex.event import EventType from reflex.event import EventType, identity_event
from reflex.style import Style from reflex.style import Style
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import RadixThemesComponent from ..base import RadixThemesComponent
def on_value_event_spec( on_value_event_spec = (
value: Var[List[int | float]], identity_event(list[Union[int, float]]),
) -> Tuple[Var[List[int | float]]]: ... identity_event(list[int]),
identity_event(list[float]),
)
class Slider(RadixThemesComponent): class Slider(RadixThemesComponent):
@overload @overload
@ -22,7 +24,6 @@ class Slider(RadixThemesComponent):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
width: Optional[str] = "100%",
as_child: Optional[Union[Var[bool], bool]] = None, as_child: Optional[Union[Var[bool], bool]] = None,
size: Optional[ size: Optional[
Union[ Union[
@ -121,6 +122,7 @@ class Slider(RadixThemesComponent):
Union[List[Union[float, int]], Var[List[Union[float, int]]]] Union[List[Union[float, int]], Var[List[Union[float, int]]]]
] = None, ] = None,
name: Optional[Union[Var[str], str]] = None, name: Optional[Union[Var[str], str]] = None,
width: Optional[Union[Var[Optional[str]], str]] = None,
min: Optional[Union[Var[Union[float, int]], float, int]] = None, min: Optional[Union[Var[Union[float, int]], float, int]] = None,
max: Optional[Union[Var[Union[float, int]], float, int]] = None, max: Optional[Union[Var[Union[float, int]], float, int]] = None,
step: Optional[Union[Var[Union[float, int]], float, int]] = None, step: Optional[Union[Var[Union[float, int]], float, int]] = None,
@ -138,7 +140,13 @@ class Slider(RadixThemesComponent):
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[EventType[[]]] = None, on_blur: Optional[EventType[[]]] = None,
on_change: Optional[EventType[List[int | float]]] = None, on_change: Optional[
Union[
EventType[list[Union[int, float]]],
EventType[list[int]],
EventType[list[float]],
]
] = None,
on_click: Optional[EventType[[]]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None,
on_double_click: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None,
@ -153,14 +161,19 @@ class Slider(RadixThemesComponent):
on_mouse_up: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_unmount: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None,
on_value_commit: Optional[EventType[List[int | float]]] = None, on_value_commit: Optional[
Union[
EventType[list[Union[int, float]]],
EventType[list[int]],
EventType[list[float]],
]
] = None,
**props, **props,
) -> "Slider": ) -> "Slider":
"""Create a Slider component. """Create a Slider component.
Args: Args:
*children: The children of the component. *children: The children of the component.
width: The width of the slider.
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
size: Button size "1" - "3" size: Button size "1" - "3"
variant: Variant of button variant: Variant of button
@ -170,11 +183,14 @@ class Slider(RadixThemesComponent):
default_value: The value of the slider when initially rendered. Use when you do not need to control the state of the slider. default_value: The value of the slider when initially rendered. Use when you do not need to control the state of the slider.
value: The controlled value of the slider. Must be used in conjunction with onValueChange. value: The controlled value of the slider. Must be used in conjunction with onValueChange.
name: The name of the slider. Submitted with its owning form as part of a name/value pair. name: The name of the slider. Submitted with its owning form as part of a name/value pair.
width: The width of the slider.
min: The minimum value of the slider. min: The minimum value of the slider.
max: The maximum value of the slider. max: The maximum value of the slider.
step: The step value of the slider. step: The step value of the slider.
disabled: Whether the slider is disabled disabled: Whether the slider is disabled
orientation: The orientation of the slider. orientation: The orientation of the slider.
on_change: Props to rename Fired when the value of the slider changes.
on_value_commit: Fired when a thumb is released after being dragged.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -155,6 +155,7 @@ class Switch(RadixThemesComponent):
color_scheme: Override theme color for switch color_scheme: Override theme color for switch
high_contrast: Whether to render the switch with higher contrast color against background high_contrast: Whether to render the switch with higher contrast color against background
radius: Override theme radius for switch: "none" | "small" | "full" radius: Override theme radius for switch: "none" | "small" | "full"
on_change: Props to rename Fired when the value of the switch changes
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -70,6 +70,7 @@ class TabsRoot(RadixThemesComponent):
orientation: The orientation of the tabs. orientation: The orientation of the tabs.
dir: Reading direction of the tabs. dir: Reading direction of the tabs.
activation_mode: The mode of activation for the tabs. "automatic" will activate the tab when focused. "manual" will activate the tab when clicked. activation_mode: The mode of activation for the tabs. "automatic" will activate the tab when focused. "manual" will activate the tab when clicked.
on_change: Props to rename Fired when the value of the tabs changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -369,6 +370,7 @@ class Tabs(ComponentNamespace):
orientation: The orientation of the tabs. orientation: The orientation of the tabs.
dir: Reading direction of the tabs. dir: Reading direction of the tabs.
activation_mode: The mode of activation for the tabs. "automatic" will activate the tab when focused. "manual" will activate the tab when clicked. activation_mode: The mode of activation for the tabs. "automatic" will activate the tab when focused. "manual" will activate the tab when clicked.
on_change: Props to rename Fired when the value of the tabs changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -214,6 +214,11 @@ class TextArea(RadixThemesComponent, elements.Textarea):
auto_height: Automatically fit the content height to the text (use min-height with this prop) auto_height: Automatically fit the content height to the text (use min-height with this prop)
cols: Visible width of the text control, in average character widths cols: Visible width of the text control, in average character widths
enter_key_submit: Enter key submits form (shift-enter adds new line) enter_key_submit: Enter key submits form (shift-enter adds new line)
on_change: Fired when the input value changes
on_focus: Fired when the input gains focus
on_blur: Fired when the input loses focus
on_key_down: Fired when a key is pressed down
on_key_up: Fired when a key is released
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.

View File

@ -188,6 +188,11 @@ class TextFieldRoot(elements.Div, RadixThemesComponent):
required: Indicates that the input is required required: Indicates that the input is required
type: Specifies the type of input type: Specifies the type of input
value: Value of the input value: Value of the input
on_change: Fired when the value of the textarea changes.
on_focus: Fired when the textarea is focused.
on_blur: Fired when the textarea is blurred.
on_key_down: Fired when a key is pressed down.
on_key_up: Fired when a key is released.
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
@ -500,6 +505,11 @@ class TextField(ComponentNamespace):
required: Indicates that the input is required required: Indicates that the input is required
type: Specifies the type of input type: Specifies the type of input
value: Value of the input value: Value of the input
on_change: Fired when the value of the textarea changes.
on_focus: Fired when the textarea is focused.
on_blur: Fired when the textarea is blurred.
on_key_down: Fired when a key is pressed down.
on_key_up: Fired when a key is released.
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.

View File

@ -106,6 +106,9 @@ class Tooltip(RadixThemesComponent):
disable_hoverable_content: Prevents Tooltip content from remaining open when hovering. disable_hoverable_content: Prevents Tooltip content from remaining open when hovering.
force_mount: Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. force_mount: Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries.
aria_label: By default, screenreaders will announce the content inside the component. If this is not descriptive enough, or you have content that cannot be announced, use aria-label as a more descriptive label. aria_label: By default, screenreaders will announce the content inside the component. If this is not descriptive enough, or you have content that cannot be announced, use aria-label as a more descriptive label.
on_open_change: Fired when the open state changes.
on_escape_key_down: Fired when the escape key is pressed.
on_pointer_down_outside: Fired when the pointer is down outside the tooltip.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -2,7 +2,7 @@
from __future__ import annotations from __future__ import annotations
from typing import Any, Iterable, Literal, Optional, Union from typing import Any, Iterable, Literal, Union
from reflex.components.component import Component, ComponentNamespace from reflex.components.component import Component, ComponentNamespace
from reflex.components.core.foreach import Foreach from reflex.components.core.foreach import Foreach
@ -44,27 +44,30 @@ class BaseList(Component):
# The style of the list. Default to "none". # The style of the list. Default to "none".
list_style_type: Var[ list_style_type: Var[
Union[LiteralListStyleTypeUnordered, LiteralListStyleTypeOrdered] Union[LiteralListStyleTypeUnordered, LiteralListStyleTypeOrdered]
] ] = Var.create("none")
# A list of items to add to the list.
items: Var[Iterable] = Var.create([])
@classmethod @classmethod
def create( def create(
cls, cls,
*children, *children,
items: Optional[Var[Iterable]] = None,
**props, **props,
): ):
"""Create a list component. """Create a list component.
Args: Args:
*children: The children of the component. *children: The children of the component.
items: A list of items to add to the list.
**props: The properties of the component. **props: The properties of the component.
Returns: Returns:
The list component. The list component.
""" """
items = props.pop("items", None)
list_style_type = props.pop("list_style_type", "none") list_style_type = props.pop("list_style_type", "none")
if not children and items is not None: if not children and items is not None:
if isinstance(items, Var): if isinstance(items, Var):
children = [Foreach.create(items, ListItem.create)] children = [Foreach.create(items, ListItem.create)]
@ -87,6 +90,9 @@ class BaseList(Component):
"direction": "column", "direction": "column",
} }
def _exclude_props(self) -> list[str]:
return ["items", "list_style_type"]
class UnorderedList(BaseList, Ul): class UnorderedList(BaseList, Ul):
"""Display an unordered list.""" """Display an unordered list."""
@ -97,22 +103,21 @@ class UnorderedList(BaseList, Ul):
def create( def create(
cls, cls,
*children, *children,
items: Optional[Var[Iterable]] = None,
list_style_type: LiteralListStyleTypeUnordered = "disc",
**props, **props,
): ):
"""Create a unordered list component. """Create an unordered list component.
Args: Args:
*children: The children of the component. *children: The children of the component.
items: A list of items to add to the list.
list_style_type: The style of the list.
**props: The properties of the component. **props: The properties of the component.
Returns: Returns:
The list component. The list component.
""" """
items = props.pop("items", None)
list_style_type = props.pop("list_style_type", "disc")
props["margin_left"] = props.get("margin_left", "1.5rem") props["margin_left"] = props.get("margin_left", "1.5rem")
return super().create( return super().create(
*children, items=items, list_style_type=list_style_type, **props *children, items=items, list_style_type=list_style_type, **props
@ -128,22 +133,21 @@ class OrderedList(BaseList, Ol):
def create( def create(
cls, cls,
*children, *children,
items: Optional[Var[Iterable]] = None,
list_style_type: LiteralListStyleTypeOrdered = "decimal",
**props, **props,
): ):
"""Create an ordered list component. """Create an ordered list component.
Args: Args:
*children: The children of the component. *children: The children of the component.
items: A list of items to add to the list.
list_style_type: The style of the list.
**props: The properties of the component. **props: The properties of the component.
Returns: Returns:
The list component. The list component.
""" """
items = props.pop("items", None)
list_style_type = props.pop("list_style_type", "decimal")
props["margin_left"] = props.get("margin_left", "1.5rem") props["margin_left"] = props.get("margin_left", "1.5rem")
return super().create( return super().create(
*children, items=items, list_style_type=list_style_type, **props *children, items=items, list_style_type=list_style_type, **props

View File

@ -35,7 +35,6 @@ class BaseList(Component):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
items: Optional[Union[Iterable, Var[Iterable]]] = None,
list_style_type: Optional[ list_style_type: Optional[
Union[ Union[
Literal[ Literal[
@ -78,6 +77,7 @@ class BaseList(Component):
], ],
] ]
] = None, ] = None,
items: Optional[Union[Iterable, Var[Iterable]]] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
id: Optional[Any] = None, id: Optional[Any] = None,
@ -105,8 +105,8 @@ class BaseList(Component):
Args: Args:
*children: The children of the component. *children: The children of the component.
items: A list of items to add to the list.
list_style_type: The style of the list. Default to "none". list_style_type: The style of the list. Default to "none".
items: A list of items to add to the list.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -129,8 +129,49 @@ class UnorderedList(BaseList, Ul):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
list_style_type: Optional[
Union[
Literal[
"armenian",
"decimal",
"decimal-leading-zero",
"georgian",
"hiragana",
"katakana",
"lower-alpha",
"lower-greek",
"lower-latin",
"lower-roman",
"none",
"upper-alpha",
"upper-latin",
"upper-roman",
],
Literal["circle", "disc", "none", "square"],
Var[
Union[
Literal[
"armenian",
"decimal",
"decimal-leading-zero",
"georgian",
"hiragana",
"katakana",
"lower-alpha",
"lower-greek",
"lower-latin",
"lower-roman",
"none",
"upper-alpha",
"upper-latin",
"upper-roman",
],
Literal["circle", "disc", "none", "square"],
]
],
]
] = None,
items: Optional[Union[Iterable, Var[Iterable]]] = None, items: Optional[Union[Iterable, Var[Iterable]]] = None,
list_style_type: Optional[LiteralListStyleTypeUnordered] = "disc",
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None, access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_capitalize: Optional[ auto_capitalize: Optional[
Union[Var[Union[bool, int, str]], bool, int, str] Union[Var[Union[bool, int, str]], bool, int, str]
@ -178,12 +219,12 @@ class UnorderedList(BaseList, Ul):
on_unmount: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None,
**props, **props,
) -> "UnorderedList": ) -> "UnorderedList":
"""Create a unordered list component. """Create an unordered list component.
Args: Args:
*children: The children of the component. *children: The children of the component.
list_style_type: The style of the list. Default to "none".
items: A list of items to add to the list. items: A list of items to add to the list.
list_style_type: The style of the list.
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
@ -220,8 +261,49 @@ class OrderedList(BaseList, Ol):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
list_style_type: Optional[
Union[
Literal[
"armenian",
"decimal",
"decimal-leading-zero",
"georgian",
"hiragana",
"katakana",
"lower-alpha",
"lower-greek",
"lower-latin",
"lower-roman",
"none",
"upper-alpha",
"upper-latin",
"upper-roman",
],
Literal["circle", "disc", "none", "square"],
Var[
Union[
Literal[
"armenian",
"decimal",
"decimal-leading-zero",
"georgian",
"hiragana",
"katakana",
"lower-alpha",
"lower-greek",
"lower-latin",
"lower-roman",
"none",
"upper-alpha",
"upper-latin",
"upper-roman",
],
Literal["circle", "disc", "none", "square"],
]
],
]
] = None,
items: Optional[Union[Iterable, Var[Iterable]]] = None, items: Optional[Union[Iterable, Var[Iterable]]] = None,
list_style_type: Optional[LiteralListStyleTypeOrdered] = "decimal",
reversed: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None, reversed: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
start: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None, start: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
type: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None, type: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
@ -276,8 +358,8 @@ class OrderedList(BaseList, Ol):
Args: Args:
*children: The children of the component. *children: The children of the component.
list_style_type: The style of the list. Default to "none".
items: A list of items to add to the list. items: A list of items to add to the list.
list_style_type: The style of the list.
reversed: Reverses the order of the list. reversed: Reverses the order of the list.
start: Specifies the start value of the first list item in an ordered list. start: Specifies the start value of the first list item in an ordered list.
type: Specifies the kind of marker to use in the list (letters or numbers). type: Specifies the kind of marker to use in the list (letters or numbers).
@ -406,7 +488,6 @@ class List(ComponentNamespace):
@staticmethod @staticmethod
def __call__( def __call__(
*children, *children,
items: Optional[Union[Iterable, Var[Iterable]]] = None,
list_style_type: Optional[ list_style_type: Optional[
Union[ Union[
Literal[ Literal[
@ -449,6 +530,7 @@ class List(ComponentNamespace):
], ],
] ]
] = None, ] = None,
items: Optional[Union[Iterable, Var[Iterable]]] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
id: Optional[Any] = None, id: Optional[Any] = None,
@ -476,8 +558,8 @@ class List(ComponentNamespace):
Args: Args:
*children: The children of the component. *children: The children of the component.
items: A list of items to add to the list.
list_style_type: The style of the list. Default to "none". list_style_type: The style of the list. Default to "none".
items: A list of items to add to the list.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -12,20 +12,22 @@ from .flex import Flex, LiteralFlexDirection
class Stack(Flex): class Stack(Flex):
"""A stack component.""" """A stack component."""
# The spacing between each stack item.
spacing: Var[LiteralSpacing] = Var.create("3")
# The alignment of the stack items.
align: Var[LiteralAlign] = Var.create("start")
@classmethod @classmethod
def create( def create(
cls, cls,
*children, *children,
spacing: LiteralSpacing = "3",
align: LiteralAlign = "start",
**props, **props,
) -> Component: ) -> Component:
"""Create a new instance of the component. """Create a new instance of the component.
Args: Args:
*children: The children of the stack. *children: The children of the stack.
spacing: The spacing between each stack item.
align: The alignment of the stack items.
**props: The properties of the stack. **props: The properties of the stack.
Returns: Returns:
@ -39,8 +41,6 @@ class Stack(Flex):
return super().create( return super().create(
*children, *children,
spacing=spacing,
align=align,
**props, **props,
) )

View File

@ -10,7 +10,6 @@ from reflex.event import EventType
from reflex.style import Style from reflex.style import Style
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import LiteralAlign, LiteralSpacing
from .flex import Flex from .flex import Flex
class Stack(Flex): class Stack(Flex):
@ -19,8 +18,18 @@ class Stack(Flex):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
spacing: Optional[LiteralSpacing] = "3", spacing: Optional[
align: Optional[LiteralAlign] = "start", Union[
Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
Var[Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]],
]
] = None,
align: Optional[
Union[
Literal["baseline", "center", "end", "start", "stretch"],
Var[Literal["baseline", "center", "end", "start", "stretch"]],
]
] = None,
as_child: Optional[Union[Var[bool], bool]] = None, as_child: Optional[Union[Var[bool], bool]] = None,
direction: Optional[ direction: Optional[
Union[ Union[
@ -114,8 +123,8 @@ class Stack(Flex):
Args: Args:
*children: The children of the stack. *children: The children of the stack.
spacing: The spacing between each stack item. spacing: Gap between children: "0" - "9"
align: The alignment of the stack items. align: Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch"
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
direction: How child items are layed out: "row" | "column" | "row-reverse" | "column-reverse" direction: How child items are layed out: "row" | "column" | "row-reverse" | "column-reverse"
justify: Alignment of children along the cross axis: "start" | "center" | "end" | "between" justify: Alignment of children along the cross axis: "start" | "center" | "end" | "between"
@ -155,14 +164,24 @@ class VStack(Stack):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
spacing: Optional[LiteralSpacing] = "3",
align: Optional[LiteralAlign] = "start",
direction: Optional[ direction: Optional[
Union[ Union[
Literal["column", "column-reverse", "row", "row-reverse"], Literal["column", "column-reverse", "row", "row-reverse"],
Var[Literal["column", "column-reverse", "row", "row-reverse"]], Var[Literal["column", "column-reverse", "row", "row-reverse"]],
] ]
] = None, ] = None,
spacing: Optional[
Union[
Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
Var[Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]],
]
] = None,
align: Optional[
Union[
Literal["baseline", "center", "end", "start", "stretch"],
Var[Literal["baseline", "center", "end", "start", "stretch"]],
]
] = None,
as_child: Optional[Union[Var[bool], bool]] = None, as_child: Optional[Union[Var[bool], bool]] = None,
justify: Optional[ justify: Optional[
Union[ Union[
@ -239,9 +258,9 @@ class VStack(Stack):
Args: Args:
*children: The children of the stack. *children: The children of the stack.
spacing: The spacing between each stack item.
align: The alignment of the stack items.
direction: How child items are layed out: "row" | "column" | "row-reverse" | "column-reverse" direction: How child items are layed out: "row" | "column" | "row-reverse" | "column-reverse"
spacing: Gap between children: "0" - "9"
align: Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch"
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
justify: Alignment of children along the cross axis: "start" | "center" | "end" | "between" justify: Alignment of children along the cross axis: "start" | "center" | "end" | "between"
wrap: Whether children should wrap when they reach the end of their container: "nowrap" | "wrap" | "wrap-reverse" wrap: Whether children should wrap when they reach the end of their container: "nowrap" | "wrap" | "wrap-reverse"
@ -280,14 +299,24 @@ class HStack(Stack):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
spacing: Optional[LiteralSpacing] = "3",
align: Optional[LiteralAlign] = "start",
direction: Optional[ direction: Optional[
Union[ Union[
Literal["column", "column-reverse", "row", "row-reverse"], Literal["column", "column-reverse", "row", "row-reverse"],
Var[Literal["column", "column-reverse", "row", "row-reverse"]], Var[Literal["column", "column-reverse", "row", "row-reverse"]],
] ]
] = None, ] = None,
spacing: Optional[
Union[
Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
Var[Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]],
]
] = None,
align: Optional[
Union[
Literal["baseline", "center", "end", "start", "stretch"],
Var[Literal["baseline", "center", "end", "start", "stretch"]],
]
] = None,
as_child: Optional[Union[Var[bool], bool]] = None, as_child: Optional[Union[Var[bool], bool]] = None,
justify: Optional[ justify: Optional[
Union[ Union[
@ -364,9 +393,9 @@ class HStack(Stack):
Args: Args:
*children: The children of the stack. *children: The children of the stack.
spacing: The spacing between each stack item.
align: The alignment of the stack items.
direction: How child items are layed out: "row" | "column" | "row-reverse" | "column-reverse" direction: How child items are layed out: "row" | "column" | "row-reverse" | "column-reverse"
spacing: Gap between children: "0" - "9"
align: Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch"
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
justify: Alignment of children along the cross axis: "start" | "center" | "end" | "between" justify: Alignment of children along the cross axis: "start" | "center" | "end" | "between"
wrap: Whether children should wrap when they reach the end of their container: "nowrap" | "wrap" | "wrap-reverse" wrap: Whether children should wrap when they reach the end of their container: "nowrap" | "wrap" | "wrap-reverse"

View File

@ -82,6 +82,22 @@ class Audio(ReactPlayer):
muted: Mutes the player muted: Mutes the player
width: Set the width of the player: ex:640px width: Set the width of the player: ex:640px
height: Set the height of the player: ex:640px height: Set the height of the player: ex:640px
on_ready: Called when media is loaded and ready to play. If playing is set to true, media will play immediately.
on_start: Called when media starts playing.
on_play: Called when media starts or resumes playing after pausing or buffering.
on_progress: Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 }
on_duration: Callback containing duration of the media, in seconds.
on_pause: Called when media is paused.
on_buffer: Called when media starts buffering.
on_buffer_end: Called when media has finished buffering. Works for files, YouTube and Facebook.
on_seek: Called when media seeks with seconds parameter.
on_playback_rate_change: Called when playback rate of the player changed. Only supported by YouTube, Vimeo (if enabled), Wistia, and file paths.
on_playback_quality_change: Called when playback quality of the player changed. Only supported by YouTube (if enabled).
on_ended: Called when media finishes playing. Does not fire when loop is set to true.
on_error: Called when an error occurs whilst attempting to play media.
on_click_preview: Called when user clicks the light mode preview.
on_enable_pip: Called when picture-in-picture mode is enabled.
on_disable_pip: Called when picture-in-picture mode is disabled.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -85,6 +85,22 @@ class ReactPlayer(NoSSRComponent):
muted: Mutes the player muted: Mutes the player
width: Set the width of the player: ex:640px width: Set the width of the player: ex:640px
height: Set the height of the player: ex:640px height: Set the height of the player: ex:640px
on_ready: Called when media is loaded and ready to play. If playing is set to true, media will play immediately.
on_start: Called when media starts playing.
on_play: Called when media starts or resumes playing after pausing or buffering.
on_progress: Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 }
on_duration: Callback containing duration of the media, in seconds.
on_pause: Called when media is paused.
on_buffer: Called when media starts buffering.
on_buffer_end: Called when media has finished buffering. Works for files, YouTube and Facebook.
on_seek: Called when media seeks with seconds parameter.
on_playback_rate_change: Called when playback rate of the player changed. Only supported by YouTube, Vimeo (if enabled), Wistia, and file paths.
on_playback_quality_change: Called when playback quality of the player changed. Only supported by YouTube (if enabled).
on_ended: Called when media finishes playing. Does not fire when loop is set to true.
on_error: Called when an error occurs whilst attempting to play media.
on_click_preview: Called when user clicks the light mode preview.
on_enable_pip: Called when picture-in-picture mode is enabled.
on_disable_pip: Called when picture-in-picture mode is disabled.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -82,6 +82,22 @@ class Video(ReactPlayer):
muted: Mutes the player muted: Mutes the player
width: Set the width of the player: ex:640px width: Set the width of the player: ex:640px
height: Set the height of the player: ex:640px height: Set the height of the player: ex:640px
on_ready: Called when media is loaded and ready to play. If playing is set to true, media will play immediately.
on_start: Called when media starts playing.
on_play: Called when media starts or resumes playing after pausing or buffering.
on_progress: Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 }
on_duration: Callback containing duration of the media, in seconds.
on_pause: Called when media is paused.
on_buffer: Called when media starts buffering.
on_buffer_end: Called when media has finished buffering. Works for files, YouTube and Facebook.
on_seek: Called when media seeks with seconds parameter.
on_playback_rate_change: Called when playback rate of the player changed. Only supported by YouTube, Vimeo (if enabled), Wistia, and file paths.
on_playback_quality_change: Called when playback quality of the player changed. Only supported by YouTube (if enabled).
on_ended: Called when media finishes playing. Does not fire when loop is set to true.
on_error: Called when an error occurs whilst attempting to play media.
on_click_preview: Called when user clicks the light mode preview.
on_enable_pip: Called when picture-in-picture mode is enabled.
on_disable_pip: Called when picture-in-picture mode is disabled.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -168,6 +168,13 @@ class Axis(Recharts):
min_tick_gap: The minimum gap between two adjacent labels. Default: 5 min_tick_gap: The minimum gap between two adjacent labels. Default: 5
stroke: The stroke color of axis. Default: rx.color("gray", 9) stroke: The stroke color of axis. Default: rx.color("gray", 9)
text_anchor: The text anchor of axis. Default: "middle" text_anchor: The text anchor of axis. Default: "middle"
on_click: The customized event handler of click on the ticks of this axis
on_mouse_down: The customized event handler of mousedown on the ticks of this axis
on_mouse_up: The customized event handler of mouseup on the ticks of this axis
on_mouse_move: The customized event handler of mousemove on the ticks of this axis
on_mouse_out: The customized event handler of mouseout on the ticks of this axis
on_mouse_enter: The customized event handler of mouseenter on the ticks of this axis
on_mouse_leave: The customized event handler of mouseleave on the ticks of this axis
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -347,6 +354,13 @@ class XAxis(Axis):
min_tick_gap: The minimum gap between two adjacent labels. Default: 5 min_tick_gap: The minimum gap between two adjacent labels. Default: 5
stroke: The stroke color of axis. Default: rx.color("gray", 9) stroke: The stroke color of axis. Default: rx.color("gray", 9)
text_anchor: The text anchor of axis. Default: "middle" text_anchor: The text anchor of axis. Default: "middle"
on_click: The customized event handler of click on the ticks of this axis
on_mouse_down: The customized event handler of mousedown on the ticks of this axis
on_mouse_up: The customized event handler of mouseup on the ticks of this axis
on_mouse_move: The customized event handler of mousemove on the ticks of this axis
on_mouse_out: The customized event handler of mouseout on the ticks of this axis
on_mouse_enter: The customized event handler of mouseenter on the ticks of this axis
on_mouse_leave: The customized event handler of mouseleave on the ticks of this axis
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -522,6 +536,13 @@ class YAxis(Axis):
min_tick_gap: The minimum gap between two adjacent labels. Default: 5 min_tick_gap: The minimum gap between two adjacent labels. Default: 5
stroke: The stroke color of axis. Default: rx.color("gray", 9) stroke: The stroke color of axis. Default: rx.color("gray", 9)
text_anchor: The text anchor of axis. Default: "middle" text_anchor: The text anchor of axis. Default: "middle"
on_click: The customized event handler of click on the ticks of this axis
on_mouse_down: The customized event handler of mousedown on the ticks of this axis
on_mouse_up: The customized event handler of mouseup on the ticks of this axis
on_mouse_move: The customized event handler of mousemove on the ticks of this axis
on_mouse_out: The customized event handler of mouseout on the ticks of this axis
on_mouse_enter: The customized event handler of mouseenter on the ticks of this axis
on_mouse_leave: The customized event handler of mouseleave on the ticks of this axis
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -787,6 +808,16 @@ class Cartesian(Recharts):
animation_easing: The type of easing function. Default: "ease" animation_easing: The type of easing function. Default: "ease"
unit: The unit of data. This option will be used in tooltip. unit: The unit of data. This option will be used in tooltip.
name: The name of data. This option will be used in tooltip and legend to represent the component. If no value was set to this option, the value of dataKey will be used alternatively. name: The name of data. This option will be used in tooltip and legend to represent the component. If no value was set to this option, the value of dataKey will be used alternatively.
on_animation_start: The customized event handler of animation start
on_animation_end: The customized event handler of animation end
on_click: The customized event handler of click on the component in this group
on_mouse_down: The customized event handler of mousedown on the component in this group
on_mouse_up: The customized event handler of mouseup on the component in this group
on_mouse_move: The customized event handler of mousemove on the component in this group
on_mouse_over: The customized event handler of mouseover on the component in this group
on_mouse_out: The customized event handler of mouseout on the component in this group
on_mouse_enter: The customized event handler of mouseenter on the component in this group
on_mouse_leave: The customized event handler of mouseleave on the component in this group
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -965,6 +996,16 @@ class Area(Cartesian):
animation_easing: The type of easing function. Default: "ease" animation_easing: The type of easing function. Default: "ease"
unit: The unit of data. This option will be used in tooltip. unit: The unit of data. This option will be used in tooltip.
name: The name of data. This option will be used in tooltip and legend to represent the component. If no value was set to this option, the value of dataKey will be used alternatively. name: The name of data. This option will be used in tooltip and legend to represent the component. If no value was set to this option, the value of dataKey will be used alternatively.
on_animation_start: The customized event handler of animation start
on_animation_end: The customized event handler of animation end
on_click: The customized event handler of click on the component in this group
on_mouse_down: The customized event handler of mousedown on the component in this group
on_mouse_up: The customized event handler of mouseup on the component in this group
on_mouse_move: The customized event handler of mousemove on the component in this group
on_mouse_over: The customized event handler of mouseover on the component in this group
on_mouse_out: The customized event handler of mouseout on the component in this group
on_mouse_enter: The customized event handler of mouseenter on the component in this group
on_mouse_leave: The customized event handler of mouseleave on the component in this group
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -1096,6 +1137,16 @@ class Bar(Cartesian):
animation_begin: Specifies when the animation should begin, the unit of this option is ms. Default: 0 animation_begin: Specifies when the animation should begin, the unit of this option is ms. Default: 0
animation_duration: Specifies the duration of animation, the unit of this option is ms. Default: 1500 animation_duration: Specifies the duration of animation, the unit of this option is ms. Default: 1500
animation_easing: The type of easing function. Default: "ease" animation_easing: The type of easing function. Default: "ease"
on_animation_start: The customized event handler of animation start
on_animation_end: The customized event handler of animation end
on_click: The customized event handler of click on the component in this group
on_mouse_down: The customized event handler of mousedown on the component in this group
on_mouse_up: The customized event handler of mouseup on the component in this group
on_mouse_move: The customized event handler of mousemove on the component in this group
on_mouse_over: The customized event handler of mouseover on the component in this group
on_mouse_out: The customized event handler of mouseout on the component in this group
on_mouse_enter: The customized event handler of mouseenter on the component in this group
on_mouse_leave: The customized event handler of mouseleave on the component in this group
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -1270,6 +1321,16 @@ class Line(Cartesian):
animation_duration: Specifies the duration of animation, the unit of this option is ms. Default: 1500 animation_duration: Specifies the duration of animation, the unit of this option is ms. Default: 1500
animation_easing: The type of easing function. Default: "ease" animation_easing: The type of easing function. Default: "ease"
name: The name of data. This option will be used in tooltip and legend to represent the component. If no value was set to this option, the value of dataKey will be used alternatively. name: The name of data. This option will be used in tooltip and legend to represent the component. If no value was set to this option, the value of dataKey will be used alternatively.
on_animation_start: The customized event handler of animation start
on_animation_end: The customized event handler of animation end
on_click: The customized event handler of click on the component in this group
on_mouse_down: The customized event handler of mousedown on the component in this group
on_mouse_up: The customized event handler of mouseup on the component in this group
on_mouse_move: The customized event handler of mousemove on the component in this group
on_mouse_over: The customized event handler of mouseover on the component in this group
on_mouse_out: The customized event handler of mouseout on the component in this group
on_mouse_enter: The customized event handler of mouseenter on the component in this group
on_mouse_leave: The customized event handler of mouseleave on the component in this group
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -1397,6 +1458,14 @@ class Scatter(Recharts):
animation_begin: Specifies when the animation should begin, the unit of this option is ms. Default: 0 animation_begin: Specifies when the animation should begin, the unit of this option is ms. Default: 0
animation_duration: Specifies the duration of animation, the unit of this option is ms. Default: 1500 animation_duration: Specifies the duration of animation, the unit of this option is ms. Default: 1500
animation_easing: The type of easing function. Default: "ease" animation_easing: The type of easing function. Default: "ease"
on_click: The customized event handler of click on the component in this group
on_mouse_down: The customized event handler of mousedown on the component in this group
on_mouse_up: The customized event handler of mouseup on the component in this group
on_mouse_move: The customized event handler of mousemove on the component in this group
on_mouse_over: The customized event handler of mouseover on the component in this group
on_mouse_out: The customized event handler of mouseout on the component in this group
on_mouse_enter: The customized event handler of mouseenter on the component in this group
on_mouse_leave: The customized event handler of mouseleave on the component in this group
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -1503,6 +1572,16 @@ class Funnel(Recharts):
animation_easing: The type of easing function. 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'. Default "ease" animation_easing: The type of easing function. 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'. Default "ease"
stroke: Stroke color. Default: rx.color("gray", 3) stroke: Stroke color. Default: rx.color("gray", 3)
trapezoids: The coordinates of all the trapezoids in the funnel, usually calculated internally. trapezoids: The coordinates of all the trapezoids in the funnel, usually calculated internally.
on_animation_start: The customized event handler of animation start
on_animation_end: The customized event handler of animation end
on_click: The customized event handler of click on the component in this group
on_mouse_down: The customized event handler of mousedown on the component in this group
on_mouse_up: The customized event handler of mouseup on the component in this group
on_mouse_move: The customized event handler of mousemove on the component in this group
on_mouse_over: The customized event handler of mouseover on the component in this group
on_mouse_out: The customized event handler of mouseout on the component in this group
on_mouse_enter: The customized event handler of mouseenter on the component in this group
on_mouse_leave: The customized event handler of mouseleave on the component in this group
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -1757,6 +1836,14 @@ class ReferenceDot(Reference):
r: The radius of dot. r: The radius of dot.
fill: The color of the area fill. fill: The color of the area fill.
stroke: The color of the line stroke. stroke: The color of the line stroke.
on_click: The customized event handler of click on the component in this chart
on_mouse_down: The customized event handler of mousedown on the component in this chart
on_mouse_up: The customized event handler of mouseup on the component in this chart
on_mouse_over: The customized event handler of mouseover on the component in this chart
on_mouse_out: The customized event handler of mouseout on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_move: The customized event handler of mousemove on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
x_axis_id: The id of x-axis which is corresponding to the data. Default: 0 x_axis_id: The id of x-axis which is corresponding to the data. Default: 0
y_axis_id: The id of y-axis which is corresponding to the data. Default: 0 y_axis_id: The id of y-axis which is corresponding to the data. Default: 0
if_overflow: Defines how to draw the reference line if it falls partly outside the canvas. If set to 'discard', the reference line will not be drawn at all. If set to 'hidden', the reference line will be clipped to the canvas. If set to 'visible', the reference line will be drawn completely. If set to 'extendDomain', the domain of the overflown axis will be extended such that the reference line fits into the canvas. Default: "discard" if_overflow: Defines how to draw the reference line if it falls partly outside the canvas. If set to 'discard', the reference line will not be drawn at all. If set to 'hidden', the reference line will be clipped to the canvas. If set to 'visible', the reference line will be drawn completely. If set to 'extendDomain', the domain of the overflown axis will be extended such that the reference line fits into the canvas. Default: "discard"

View File

@ -51,6 +51,10 @@ class ChartBase(RechartsCharts):
*children: The children of the chart component. *children: The children of the chart component.
width: The width of chart container. String or Integer width: The width of chart container. String or Integer
height: The height of chart container. height: The height of chart container.
on_click: The customized event handler of click on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_move: The customized event handler of mousemove on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -125,6 +129,10 @@ class CategoricalChartBase(ChartBase):
stack_offset: The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. 'expand' | 'none' | 'wiggle' | 'silhouette' stack_offset: The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. 'expand' | 'none' | 'wiggle' | 'silhouette'
width: The width of chart container. String or Integer width: The width of chart container. String or Integer
height: The height of chart container. height: The height of chart container.
on_click: The customized event handler of click on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_move: The customized event handler of mousemove on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -207,6 +215,10 @@ class AreaChart(CategoricalChartBase):
stack_offset: The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. 'expand' | 'none' | 'wiggle' | 'silhouette' stack_offset: The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. 'expand' | 'none' | 'wiggle' | 'silhouette'
width: The width of chart container. String or Integer width: The width of chart container. String or Integer
height: The height of chart container. height: The height of chart container.
on_click: The customized event handler of click on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_move: The customized event handler of mousemove on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -291,6 +303,10 @@ class BarChart(CategoricalChartBase):
layout: The layout of area in the chart. 'horizontal' | 'vertical'. Default: "horizontal" layout: The layout of area in the chart. 'horizontal' | 'vertical'. Default: "horizontal"
width: The width of chart container. String or Integer width: The width of chart container. String or Integer
height: The height of chart container. height: The height of chart container.
on_click: The customized event handler of click on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_move: The customized event handler of mousemove on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -365,6 +381,10 @@ class LineChart(CategoricalChartBase):
stack_offset: The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. 'expand' | 'none' | 'wiggle' | 'silhouette' stack_offset: The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. 'expand' | 'none' | 'wiggle' | 'silhouette'
width: The width of chart container. String or Integer width: The width of chart container. String or Integer
height: The height of chart container. height: The height of chart container.
on_click: The customized event handler of click on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_move: The customized event handler of mousemove on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -455,6 +475,10 @@ class ComposedChart(CategoricalChartBase):
stack_offset: The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. 'expand' | 'none' | 'wiggle' | 'silhouette' stack_offset: The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. 'expand' | 'none' | 'wiggle' | 'silhouette'
width: The width of chart container. String or Integer width: The width of chart container. String or Integer
height: The height of chart container. height: The height of chart container.
on_click: The customized event handler of click on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_move: The customized event handler of mousemove on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -505,8 +529,16 @@ class PieChart(ChartBase):
Args: Args:
*children: The children of the chart component. *children: The children of the chart component.
margin: The sizes of whitespace around the chart, i.e. {"top": 50, "right": 30, "left": 20, "bottom": 5}. margin: The sizes of whitespace around the chart, i.e. {"top": 50, "right": 30, "left": 20, "bottom": 5}.
on_mouse_down: The customized event handler of mousedown on the sectors in this group
on_mouse_up: The customized event handler of mouseup on the sectors in this group
on_mouse_over: The customized event handler of mouseover on the sectors in this group
on_mouse_out: The customized event handler of mouseout on the sectors in this group
width: The width of chart container. String or Integer width: The width of chart container. String or Integer
height: The height of chart container. height: The height of chart container.
on_click: The customized event handler of click on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_move: The customized event handler of mousemove on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -562,6 +594,9 @@ class RadarChart(ChartBase):
outer_radius: The outer radius of last circle grid. If set a percentage, the final value is obtained by multiplying the percentage of maxRadius which is calculated by the width, height, cx, cy. Number | Percentage. Default: "80%" outer_radius: The outer radius of last circle grid. If set a percentage, the final value is obtained by multiplying the percentage of maxRadius which is calculated by the width, height, cx, cy. Number | Percentage. Default: "80%"
width: The width of chart container. String or Integer width: The width of chart container. String or Integer
height: The height of chart container. height: The height of chart container.
on_click: The customized event handler of click on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -634,6 +669,10 @@ class RadialBarChart(ChartBase):
bar_size: The size of each bar. If the barSize is not specified, the size of bar will be calculated by the barCategoryGap, barGap and the quantity of bar groups. bar_size: The size of each bar. If the barSize is not specified, the size of bar will be calculated by the barCategoryGap, barGap and the quantity of bar groups.
width: The width of chart container. String or Integer width: The width of chart container. String or Integer
height: The height of chart container. height: The height of chart container.
on_click: The customized event handler of click on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_move: The customized event handler of mousemove on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -680,6 +719,10 @@ class ScatterChart(ChartBase):
margin: The sizes of whitespace around the chart. Default: {"top": 5, "right": 5, "bottom": 5, "left": 5} margin: The sizes of whitespace around the chart. Default: {"top": 5, "right": 5, "bottom": 5, "left": 5}
width: The width of chart container. String or Integer width: The width of chart container. String or Integer
height: The height of chart container. height: The height of chart container.
on_click: The customized event handler of click on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_move: The customized event handler of mousemove on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -736,6 +779,10 @@ class FunnelChart(ChartBase):
stroke: The stroke color of each bar. String | Object stroke: The stroke color of each bar. String | Object
width: The width of chart container. String or Integer width: The width of chart container. String or Integer
height: The height of chart container. height: The height of chart container.
on_click: The customized event handler of click on the component in this chart
on_mouse_enter: The customized event handler of mouseenter on the component in this chart
on_mouse_move: The customized event handler of mousemove on the component in this chart
on_mouse_leave: The customized event handler of mouseleave on the component in this chart
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -809,6 +856,8 @@ class Treemap(RechartsCharts):
animation_begin: Specifies when the animation should begin, the unit of this option is ms. Default: 0 animation_begin: Specifies when the animation should begin, the unit of this option is ms. Default: 0
animation_duration: Specifies the duration of animation, the unit of this option is ms. Default: 1500 animation_duration: Specifies the duration of animation, the unit of this option is ms. Default: 1500
animation_easing: The type of easing function. 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'. Default: "ease" animation_easing: The type of easing function. 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'. Default: "ease"
on_animation_start: The customized event handler of animation start
on_animation_end: The customized event handler of animation end
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -61,6 +61,7 @@ class ResponsiveContainer(Recharts, MemoizationLeaf):
min_width: The minimum width of chart container. Number min_width: The minimum width of chart container. Number
min_height: The minimum height of chart container. Number min_height: The minimum height of chart container. Number
debounce: If specified a positive number, debounced function will be used to handle the resize event. Default: 0 debounce: If specified a positive number, debounced function will be used to handle the resize event. Default: 0
on_resize: If specified provides a callback providing the updated chart width and height values.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -175,6 +176,14 @@ class Legend(Recharts):
chart_width: The width of chart container, usually calculated internally. chart_width: The width of chart container, usually calculated internally.
chart_height: The height of chart container, usually calculated internally. chart_height: The height of chart container, usually calculated internally.
margin: The margin of chart container, usually calculated internally. margin: The margin of chart container, usually calculated internally.
on_click: The customized event handler of click on the items in this group
on_mouse_down: The customized event handler of mousedown on the items in this group
on_mouse_up: The customized event handler of mouseup on the items in this group
on_mouse_move: The customized event handler of mousemove on the items in this group
on_mouse_over: The customized event handler of mouseover on the items in this group
on_mouse_out: The customized event handler of mouseout on the items in this group
on_mouse_enter: The customized event handler of mouseenter on the items in this group
on_mouse_leave: The customized event handler of mouseleave on the items in this group
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -391,6 +391,14 @@ class PolarAngleAxis(Recharts):
orientation: The orientation of axis text. Default: "outer" orientation: The orientation of axis text. Default: "outer"
stroke: The stroke color of axis. Default: rx.color("gray", 10) stroke: The stroke color of axis. Default: rx.color("gray", 10)
allow_duplicated_category: Allow the axis has duplicated categorys or not when the type of axis is "category". Default: True allow_duplicated_category: Allow the axis has duplicated categorys or not when the type of axis is "category". Default: True
on_click: The customized event handler of click on the ticks of this axis.
on_mouse_down: The customized event handler of mousedown on the the ticks of this axis.
on_mouse_up: The customized event handler of mouseup on the ticks of this axis.
on_mouse_move: The customized event handler of mousemove on the ticks of this axis.
on_mouse_over: The customized event handler of mouseover on the ticks of this axis.
on_mouse_out: The customized event handler of mouseout on the ticks of this axis.
on_mouse_enter: The customized event handler of moustenter on the ticks of this axis.
on_mouse_leave: The customized event handler of mouseleave on the ticks of this axis.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -4,12 +4,10 @@ from __future__ import annotations
from typing import Any, ClassVar, Literal, Optional, Union from typing import Any, ClassVar, Literal, Optional, Union
from pydantic import ValidationError
from reflex.base import Base from reflex.base import Base
from reflex.components.component import Component, ComponentNamespace from reflex.components.component import Component, ComponentNamespace
from reflex.components.lucide.icon import Icon from reflex.components.lucide.icon import Icon
from reflex.components.props import PropsBase from reflex.components.props import NoExtrasAllowedProps, PropsBase
from reflex.event import ( from reflex.event import (
EventSpec, EventSpec,
call_script, call_script,
@ -72,7 +70,7 @@ def _toast_callback_signature(toast: Var) -> list[Var]:
] ]
class ToastProps(PropsBase): class ToastProps(PropsBase, NoExtrasAllowedProps):
"""Props for the toast component.""" """Props for the toast component."""
# Toast's title, renders above the description. # Toast's title, renders above the description.
@ -132,24 +130,6 @@ class ToastProps(PropsBase):
# Function that gets called when the toast disappears automatically after it's timeout (duration` prop). # Function that gets called when the toast disappears automatically after it's timeout (duration` prop).
on_auto_close: Optional[Any] on_auto_close: Optional[Any]
def __init__(self, **kwargs):
"""Initialize the props.
Args:
kwargs: Kwargs to initialize the props.
Raises:
ValueError: If invalid props are passed on instantiation.
"""
try:
super().__init__(**kwargs)
except ValidationError as e:
invalid_fields = ", ".join([error["loc"][0] for error in e.errors()]) # type: ignore
supported_props_str = ", ".join(f'"{field}"' for field in self.get_fields())
raise ValueError(
f"Invalid prop(s) {invalid_fields} for rx.toast. Supported props are {supported_props_str}"
) from None
def dict(self, *args, **kwargs) -> dict[str, Any]: def dict(self, *args, **kwargs) -> dict[str, Any]:
"""Convert the object to a dictionary. """Convert the object to a dictionary.
@ -181,13 +161,6 @@ class ToastProps(PropsBase):
) )
return d return d
class Config:
"""Pydantic config."""
arbitrary_types_allowed = True
use_enum_values = True
extra = "forbid"
class Toaster(Component): class Toaster(Component):
"""A Toaster Component for displaying toast notifications.""" """A Toaster Component for displaying toast notifications."""
@ -281,7 +254,7 @@ class Toaster(Component):
if message == "" and ("title" not in props or "description" not in props): if message == "" and ("title" not in props or "description" not in props):
raise ValueError("Toast message or title or description must be provided.") raise ValueError("Toast message or title or description must be provided.")
if props: if props:
args = LiteralVar.create(ToastProps(**props)) args = LiteralVar.create(ToastProps(component_name="rx.toast", **props)) # type: ignore
toast = f"{toast_command}(`{message}`, {str(args)})" toast = f"{toast_command}(`{message}`, {str(args)})"
else: else:
toast = f"{toast_command}(`{message}`)" toast = f"{toast_command}(`{message}`)"

View File

@ -8,7 +8,7 @@ from typing import Any, ClassVar, Dict, Literal, Optional, Union, overload
from reflex.base import Base from reflex.base import Base
from reflex.components.component import Component, ComponentNamespace from reflex.components.component import Component, ComponentNamespace
from reflex.components.lucide.icon import Icon from reflex.components.lucide.icon import Icon
from reflex.components.props import PropsBase from reflex.components.props import NoExtrasAllowedProps, PropsBase
from reflex.event import EventSpec, EventType from reflex.event import EventSpec, EventType
from reflex.style import Style from reflex.style import Style
from reflex.utils.serializers import serializer from reflex.utils.serializers import serializer
@ -31,7 +31,7 @@ class ToastAction(Base):
@serializer @serializer
def serialize_action(action: ToastAction) -> dict: ... def serialize_action(action: ToastAction) -> dict: ...
class ToastProps(PropsBase): class ToastProps(PropsBase, NoExtrasAllowedProps):
title: Optional[Union[str, Var]] title: Optional[Union[str, Var]]
description: Optional[Union[str, Var]] description: Optional[Union[str, Var]]
close_button: Optional[bool] close_button: Optional[bool]
@ -52,11 +52,6 @@ class ToastProps(PropsBase):
def dict(self, *args, **kwargs) -> dict[str, Any]: ... def dict(self, *args, **kwargs) -> dict[str, Any]: ...
class Config:
arbitrary_types_allowed = True
use_enum_values = True
extra = "forbid"
class Toaster(Component): class Toaster(Component):
is_used: ClassVar[bool] = False is_used: ClassVar[bool] = False

View File

@ -172,6 +172,15 @@ class Editor(NoSSRComponent):
hide: Hide the editor default: False hide: Hide the editor default: False
hide_toolbar: Hide the editor toolbar default: False hide_toolbar: Hide the editor toolbar default: False
disable_toolbar: Disable the editor toolbar default: False disable_toolbar: Disable the editor toolbar default: False
on_change: Fired when the editor content changes.
on_input: Fired when the something is inputted in the editor.
on_blur: Fired when the editor loses focus.
on_load: Fired when the editor is loaded.
on_copy: Fired when the editor content is copied.
on_cut: Fired when the editor content is cut.
on_paste: Fired when the editor content is pasted.
toggle_code_view: Fired when the code view is toggled.
toggle_full_screen: Fired when the full screen mode is toggled.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -3,7 +3,7 @@
from __future__ import annotations from __future__ import annotations
import dataclasses import dataclasses
from typing import Any, Dict, List, Optional, Tuple, Union from typing import Any, Dict, List, Optional, Union
from reflex.event import EventChain from reflex.event import EventChain
from reflex.utils import format, types from reflex.utils import format, types
@ -23,9 +23,6 @@ class Tag:
# The inner contents of the tag. # The inner contents of the tag.
contents: str = "" contents: str = ""
# Args to pass to the tag.
args: Optional[Tuple[str, ...]] = None
# Special props that aren't key value pairs. # Special props that aren't key value pairs.
special_props: List[Var] = dataclasses.field(default_factory=list) special_props: List[Var] = dataclasses.field(default_factory=list)

View File

@ -12,7 +12,7 @@ import urllib.parse
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional, Set from typing import Any, Dict, List, Optional, Set
from typing_extensions import get_type_hints from typing_extensions import Annotated, get_type_hints
from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError
from reflex.utils.types import GenericType, is_union, value_inside_optional from reflex.utils.types import GenericType, is_union, value_inside_optional
@ -204,8 +204,8 @@ def interpret_int_env(value: str, field_name: str) -> int:
) from ve ) from ve
def interpret_path_env(value: str, field_name: str) -> Path: def interpret_existing_path_env(value: str, field_name: str) -> ExistingPath:
"""Interpret a path environment variable value. """Interpret a path environment variable value as an existing path.
Args: Args:
value: The environment variable value. value: The environment variable value.
@ -223,6 +223,19 @@ def interpret_path_env(value: str, field_name: str) -> Path:
return path return path
def interpret_path_env(value: str, field_name: str) -> Path:
"""Interpret a path environment variable value.
Args:
value: The environment variable value.
field_name: The field name.
Returns:
The interpreted value.
"""
return Path(value)
def interpret_enum_env(value: str, field_type: GenericType, field_name: str) -> Any: def interpret_enum_env(value: str, field_type: GenericType, field_name: str) -> Any:
"""Interpret an enum environment variable value. """Interpret an enum environment variable value.
@ -276,6 +289,8 @@ def interpret_env_var_value(
return interpret_int_env(value, field_name) return interpret_int_env(value, field_name)
elif field_type is Path: elif field_type is Path:
return interpret_path_env(value, field_name) return interpret_path_env(value, field_name)
elif field_type is ExistingPath:
return interpret_existing_path_env(value, field_name)
elif inspect.isclass(field_type) and issubclass(field_type, enum.Enum): elif inspect.isclass(field_type) and issubclass(field_type, enum.Enum):
return interpret_enum_env(value, field_type, field_name) return interpret_enum_env(value, field_type, field_name)
@ -285,6 +300,13 @@ def interpret_env_var_value(
) )
class PathExistsFlag:
"""Flag to indicate that a path must exist."""
ExistingPath = Annotated[Path, PathExistsFlag]
@dataclasses.dataclass(init=False) @dataclasses.dataclass(init=False)
class EnvironmentVariables: class EnvironmentVariables:
"""Environment variables class to instantiate environment variables.""" """Environment variables class to instantiate environment variables."""
@ -314,7 +336,7 @@ class EnvironmentVariables:
REFLEX_WEB_WORKDIR: Path = Path(constants.Dirs.WEB) REFLEX_WEB_WORKDIR: Path = Path(constants.Dirs.WEB)
# Path to the alembic config file # Path to the alembic config file
ALEMBIC_CONFIG: Path = Path(constants.ALEMBIC_CONFIG) ALEMBIC_CONFIG: ExistingPath = Path(constants.ALEMBIC_CONFIG)
# Disable SSL verification for HTTPX requests. # Disable SSL verification for HTTPX requests.
SSL_NO_VERIFY: bool = False SSL_NO_VERIFY: bool = False
@ -427,7 +449,10 @@ class Config(Base):
telemetry_enabled: bool = True telemetry_enabled: bool = True
# The bun path # The bun path
bun_path: Path = constants.Bun.DEFAULT_PATH bun_path: ExistingPath = constants.Bun.DEFAULT_PATH
# Timeout to do a production build of a frontend page.
static_page_generation_timeout: int = 60
# List of origins that are allowed to connect to the backend API. # List of origins that are allowed to connect to the backend API.
cors_allowed_origins: List[str] = ["*"] cors_allowed_origins: List[str] = ["*"]
@ -551,7 +576,7 @@ class Config(Base):
) )
# Interpret the value. # Interpret the value.
value = interpret_env_var_value(env_var, field.type_, field.name) value = interpret_env_var_value(env_var, field.outer_type_, field.name)
# Set the value. # Set the value.
updated_values[key] = value updated_values[key] = value

View File

@ -118,7 +118,7 @@ class Node(SimpleNamespace):
"""Node/ NPM constants.""" """Node/ NPM constants."""
# The Node version. # The Node version.
VERSION = "22.10.0" VERSION = "22.11.0"
# The minimum required node version. # The minimum required node version.
MIN_VERSION = "18.18.0" MIN_VERSION = "18.18.0"

View File

@ -29,8 +29,12 @@ from typing_extensions import ParamSpec, Protocol, get_args, get_origin
from reflex import constants from reflex import constants
from reflex.utils import console, format from reflex.utils import console, format
from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch from reflex.utils.exceptions import (
from reflex.utils.types import ArgsSpec, GenericType EventFnArgMismatch,
EventHandlerArgMismatch,
EventHandlerArgTypeMismatch,
)
from reflex.utils.types import ArgsSpec, GenericType, typehint_issubclass
from reflex.vars import VarData from reflex.vars import VarData
from reflex.vars.base import ( from reflex.vars.base import (
LiteralVar, LiteralVar,
@ -83,7 +87,7 @@ class Event:
BACKGROUND_TASK_MARKER = "_reflex_background_task" BACKGROUND_TASK_MARKER = "_reflex_background_task"
def background(fn): def background(fn, *, __internal_reflex_call: bool = False):
"""Decorator to mark event handler as running in the background. """Decorator to mark event handler as running in the background.
Args: Args:
@ -96,6 +100,13 @@ def background(fn):
Raises: Raises:
TypeError: If the function is not a coroutine function or async generator. TypeError: If the function is not a coroutine function or async generator.
""" """
if not __internal_reflex_call:
console.deprecate(
"background-decorator",
"Use `rx.event(background=True)` instead.",
"0.6.5",
"0.7.0",
)
if not inspect.iscoroutinefunction(fn) and not inspect.isasyncgenfunction(fn): if not inspect.iscoroutinefunction(fn) and not inspect.isasyncgenfunction(fn):
raise TypeError("Background task must be async function or generator.") raise TypeError("Background task must be async function or generator.")
setattr(fn, BACKGROUND_TASK_MARKER, True) setattr(fn, BACKGROUND_TASK_MARKER, True)
@ -394,7 +405,9 @@ class EventChain(EventActionsMixin):
default_factory=list default_factory=list
) )
args_spec: Optional[Callable] = dataclasses.field(default=None) args_spec: Optional[Union[Callable, Sequence[Callable]]] = dataclasses.field(
default=None
)
invocation: Optional[Var] = dataclasses.field(default=None) invocation: Optional[Var] = dataclasses.field(default=None)
@ -760,18 +773,25 @@ def set_focus(ref: str) -> EventSpec:
) )
def scroll_to(elem_id: str) -> EventSpec: def scroll_to(elem_id: str, align_to_top: bool | Var[bool] = True) -> EventSpec:
"""Select the id of a html element for scrolling into view. """Select the id of a html element for scrolling into view.
Args: Args:
elem_id: the id of the element elem_id: The id of the element to scroll to.
align_to_top: Whether to scroll to the top (True) or bottom (False) of the element.
Returns: Returns:
An EventSpec to scroll the page to the selected element. An EventSpec to scroll the page to the selected element.
""" """
js_code = f"document.getElementById('{elem_id}').scrollIntoView();" get_element_by_id = FunctionStringVar.create("document.getElementById")
return call_script(js_code) return call_script(
get_element_by_id(elem_id)
.call(elem_id)
.to(ObjectVar)
.scrollIntoView.to(FunctionVar)
.call(align_to_top)
)
def set_value(ref: str, value: Any) -> EventSpec: def set_value(ref: str, value: Any) -> EventSpec:
@ -1039,7 +1059,8 @@ def get_hydrate_event(state) -> str:
def call_event_handler( def call_event_handler(
event_handler: EventHandler | EventSpec, event_handler: EventHandler | EventSpec,
arg_spec: ArgsSpec, arg_spec: ArgsSpec | Sequence[ArgsSpec],
key: Optional[str] = None,
) -> EventSpec: ) -> EventSpec:
"""Call an event handler to get the event spec. """Call an event handler to get the event spec.
@ -1050,12 +1071,16 @@ def call_event_handler(
Args: Args:
event_handler: The event handler. event_handler: The event handler.
arg_spec: The lambda that define the argument(s) to pass to the event handler. arg_spec: The lambda that define the argument(s) to pass to the event handler.
key: The key to pass to the event handler.
Raises: Raises:
EventHandlerArgMismatch: if number of arguments expected by event_handler doesn't match the spec. EventHandlerArgMismatch: if number of arguments expected by event_handler doesn't match the spec.
Returns: Returns:
The event spec from calling the event handler. The event spec from calling the event handler.
# noqa: DAR401 failure
""" """
parsed_args = parse_args_spec(arg_spec) # type: ignore parsed_args = parse_args_spec(arg_spec) # type: ignore
@ -1063,19 +1088,113 @@ def call_event_handler(
# Handle partial application of EventSpec args # Handle partial application of EventSpec args
return event_handler.add_args(*parsed_args) return event_handler.add_args(*parsed_args)
args = inspect.getfullargspec(event_handler.fn).args provided_callback_fullspec = inspect.getfullargspec(event_handler.fn)
n_args = len(args) - 1 # subtract 1 for bound self arg
if n_args == len(parsed_args): provided_callback_n_args = (
return event_handler(*parsed_args) # type: ignore len(provided_callback_fullspec.args) - 1
else: ) # subtract 1 for bound self arg
if provided_callback_n_args != len(parsed_args):
raise EventHandlerArgMismatch( raise EventHandlerArgMismatch(
"The number of arguments accepted by " "The number of arguments accepted by "
f"{event_handler.fn.__qualname__} ({n_args}) " f"{event_handler.fn.__qualname__} ({provided_callback_n_args}) "
"does not match the arguments passed by the event trigger: " "does not match the arguments passed by the event trigger: "
f"{[str(v) for v in parsed_args]}\n" f"{[str(v) for v in parsed_args]}\n"
"See https://reflex.dev/docs/events/event-arguments/" "See https://reflex.dev/docs/events/event-arguments/"
) )
all_arg_spec = [arg_spec] if not isinstance(arg_spec, Sequence) else arg_spec
event_spec_return_types = list(
filter(
lambda event_spec_return_type: event_spec_return_type is not None
and get_origin(event_spec_return_type) is tuple,
(get_type_hints(arg_spec).get("return", None) for arg_spec in all_arg_spec),
)
)
if event_spec_return_types:
failures = []
for event_spec_index, event_spec_return_type in enumerate(
event_spec_return_types
):
args = get_args(event_spec_return_type)
args_types_without_vars = [
arg if get_origin(arg) is not Var else get_args(arg)[0] for arg in args
]
try:
type_hints_of_provided_callback = get_type_hints(event_handler.fn)
except NameError:
type_hints_of_provided_callback = {}
failed_type_check = False
# check that args of event handler are matching the spec if type hints are provided
for i, arg in enumerate(provided_callback_fullspec.args[1:]):
if arg not in type_hints_of_provided_callback:
continue
try:
compare_result = typehint_issubclass(
args_types_without_vars[i], type_hints_of_provided_callback[arg]
)
except TypeError:
# TODO: In 0.7.0, remove this block and raise the exception
# raise TypeError(
# f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_handler.fn.__qualname__} provided for {key}."
# ) from e
console.warn(
f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_handler.fn.__qualname__} provided for {key}."
)
compare_result = False
if compare_result:
continue
else:
failure = EventHandlerArgTypeMismatch(
f"Event handler {key} expects {args_types_without_vars[i]} for argument {arg} but got {type_hints_of_provided_callback[arg]} as annotated in {event_handler.fn.__qualname__} instead."
)
failures.append(failure)
failed_type_check = True
break
if not failed_type_check:
if event_spec_index:
args = get_args(event_spec_return_types[0])
args_types_without_vars = [
arg if get_origin(arg) is not Var else get_args(arg)[0]
for arg in args
]
expect_string = ", ".join(
repr(arg) for arg in args_types_without_vars
).replace("[", "\\[")
given_string = ", ".join(
repr(type_hints_of_provided_callback.get(arg, Any))
for arg in provided_callback_fullspec.args[1:]
).replace("[", "\\[")
console.warn(
f"Event handler {key} expects ({expect_string}) -> () but got ({given_string}) -> () as annotated in {event_handler.fn.__qualname__} instead. "
f"This may lead to unexpected behavior but is intentionally ignored for {key}."
)
return event_handler(*parsed_args)
if failures:
console.deprecate(
"Mismatched event handler argument types",
"\n".join([str(f) for f in failures]),
"0.6.5",
"0.7.0",
)
return event_handler(*parsed_args) # type: ignore
def unwrap_var_annotation(annotation: GenericType): def unwrap_var_annotation(annotation: GenericType):
"""Unwrap a Var annotation or return it as is if it's not Var[X]. """Unwrap a Var annotation or return it as is if it's not Var[X].
@ -1114,7 +1233,7 @@ def resolve_annotation(annotations: dict[str, Any], arg_name: str):
return annotation return annotation
def parse_args_spec(arg_spec: ArgsSpec): def parse_args_spec(arg_spec: ArgsSpec | Sequence[ArgsSpec]):
"""Parse the args provided in the ArgsSpec of an event trigger. """Parse the args provided in the ArgsSpec of an event trigger.
Args: Args:
@ -1123,6 +1242,8 @@ def parse_args_spec(arg_spec: ArgsSpec):
Returns: Returns:
The parsed args. The parsed args.
""" """
# if there's multiple, the first is the default
arg_spec = arg_spec[0] if isinstance(arg_spec, Sequence) else arg_spec
spec = inspect.getfullargspec(arg_spec) spec = inspect.getfullargspec(arg_spec)
annotations = get_type_hints(arg_spec) annotations = get_type_hints(arg_spec)
@ -1138,13 +1259,18 @@ def parse_args_spec(arg_spec: ArgsSpec):
) )
def check_fn_match_arg_spec(fn: Callable, arg_spec: ArgsSpec) -> List[Var]: def check_fn_match_arg_spec(
fn: Callable,
arg_spec: ArgsSpec,
key: Optional[str] = None,
) -> List[Var]:
"""Ensures that the function signature matches the passed argument specification """Ensures that the function signature matches the passed argument specification
or raises an EventFnArgMismatch if they do not. or raises an EventFnArgMismatch if they do not.
Args: Args:
fn: The function to be validated. fn: The function to be validated.
arg_spec: The argument specification for the event trigger. arg_spec: The argument specification for the event trigger.
key: The key to pass to the event handler.
Returns: Returns:
The parsed arguments from the argument specification. The parsed arguments from the argument specification.
@ -1170,7 +1296,11 @@ def check_fn_match_arg_spec(fn: Callable, arg_spec: ArgsSpec) -> List[Var]:
return parsed_args return parsed_args
def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var: def call_event_fn(
fn: Callable,
arg_spec: ArgsSpec,
key: Optional[str] = None,
) -> list[EventSpec] | Var:
"""Call a function to a list of event specs. """Call a function to a list of event specs.
The function should return a single EventSpec, a list of EventSpecs, or a The function should return a single EventSpec, a list of EventSpecs, or a
@ -1179,6 +1309,7 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var:
Args: Args:
fn: The function to call. fn: The function to call.
arg_spec: The argument spec for the event trigger. arg_spec: The argument spec for the event trigger.
key: The key to pass to the event handler.
Returns: Returns:
The event specs from calling the function or a Var. The event specs from calling the function or a Var.
@ -1191,7 +1322,7 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var:
from reflex.utils.exceptions import EventHandlerValueError from reflex.utils.exceptions import EventHandlerValueError
# Check that fn signature matches arg_spec # Check that fn signature matches arg_spec
parsed_args = check_fn_match_arg_spec(fn, arg_spec) parsed_args = check_fn_match_arg_spec(fn, arg_spec, key=key)
# Call the function with the parsed args. # Call the function with the parsed args.
out = fn(*parsed_args) out = fn(*parsed_args)
@ -1209,7 +1340,7 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var:
for e in out: for e in out:
if isinstance(e, EventHandler): if isinstance(e, EventHandler):
# An un-called EventHandler gets all of the args of the event trigger. # An un-called EventHandler gets all of the args of the event trigger.
e = call_event_handler(e, arg_spec) e = call_event_handler(e, arg_spec, key=key)
# Make sure the event spec is valid. # Make sure the event spec is valid.
if not isinstance(e, EventSpec): if not isinstance(e, EventSpec):
@ -1419,7 +1550,12 @@ class LiteralEventChainVar(ArgsFunctionOperation, LiteralVar, EventChainVar):
Returns: Returns:
The created LiteralEventChainVar instance. The created LiteralEventChainVar instance.
""" """
sig = inspect.signature(value.args_spec) # type: ignore arg_spec = (
value.args_spec[0]
if isinstance(value.args_spec, Sequence)
else value.args_spec
)
sig = inspect.signature(arg_spec) # type: ignore
if sig.parameters: if sig.parameters:
arg_def = tuple((f"_{p}" for p in sig.parameters)) arg_def = tuple((f"_{p}" for p in sig.parameters))
arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def]) arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def])
@ -1457,6 +1593,8 @@ V3 = TypeVar("V3")
V4 = TypeVar("V4") V4 = TypeVar("V4")
V5 = TypeVar("V5") V5 = TypeVar("V5")
background_event_decorator = background
if sys.version_info >= (3, 10): if sys.version_info >= (3, 10):
from typing import Concatenate from typing import Concatenate
@ -1557,32 +1695,12 @@ if sys.version_info >= (3, 10):
return partial(self.func, instance) # type: ignore return partial(self.func, instance) # type: ignore
def event_handler(func: Callable[Concatenate[Any, P], T]) -> EventCallback[P, T]:
"""Wrap a function to be used as an event.
Args:
func: The function to wrap.
Returns:
The wrapped function.
"""
return func # type: ignore
else: else:
class EventCallback(Generic[P, T]): class EventCallback(Generic[P, T]):
"""A descriptor that wraps a function to be used as an event.""" """A descriptor that wraps a function to be used as an event."""
def event_handler(func: Callable[P, T]) -> Callable[P, T]:
"""Wrap a function to be used as an event.
Args:
func: The function to wrap.
Returns:
The wrapped function.
"""
return func
G = ParamSpec("G") G = ParamSpec("G")
@ -1608,8 +1726,93 @@ class EventNamespace(types.SimpleNamespace):
EventChainVar = EventChainVar EventChainVar = EventChainVar
LiteralEventChainVar = LiteralEventChainVar LiteralEventChainVar = LiteralEventChainVar
EventType = EventType EventType = EventType
EventCallback = EventCallback
if sys.version_info >= (3, 10):
@overload
@staticmethod
def __call__(
func: None = None, *, background: bool | None = None
) -> Callable[[Callable[Concatenate[Any, P], T]], EventCallback[P, T]]: ...
@overload
@staticmethod
def __call__(
func: Callable[Concatenate[Any, P], T],
*,
background: bool | None = None,
) -> EventCallback[P, T]: ...
@staticmethod
def __call__(
func: Callable[Concatenate[Any, P], T] | None = None,
*,
background: bool | None = None,
) -> Union[
EventCallback[P, T],
Callable[[Callable[Concatenate[Any, P], T]], EventCallback[P, T]],
]:
"""Wrap a function to be used as an event.
Args:
func: The function to wrap.
background: Whether the event should be run in the background. Defaults to False.
Returns:
The wrapped function.
"""
def wrapper(func: Callable[Concatenate[Any, P], T]) -> EventCallback[P, T]:
if background is True:
return background_event_decorator(func, __internal_reflex_call=True) # type: ignore
return func # type: ignore
if func is not None:
return wrapper(func)
return wrapper
else:
@overload
@staticmethod
def __call__(
func: None = None, *, background: bool | None = None
) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
@overload
@staticmethod
def __call__(
func: Callable[P, T], *, background: bool | None = None
) -> Callable[P, T]: ...
@staticmethod
def __call__(
func: Callable[P, T] | None = None,
*,
background: bool | None = None,
) -> Union[
Callable[P, T],
Callable[[Callable[P, T]], Callable[P, T]],
]:
"""Wrap a function to be used as an event.
Args:
func: The function to wrap.
background: Whether the event should be run in the background. Defaults to False.
Returns:
The wrapped function.
"""
def wrapper(func: Callable[P, T]) -> Callable[P, T]:
if background is True:
return background_event_decorator(func, __internal_reflex_call=True) # type: ignore
return func # type: ignore
if func is not None:
return wrapper(func)
return wrapper
__call__ = staticmethod(event_handler)
get_event = staticmethod(get_event) get_event = staticmethod(get_event)
get_hydrate_event = staticmethod(get_hydrate_event) get_hydrate_event = staticmethod(get_hydrate_event)
fix_events = staticmethod(fix_events) fix_events = staticmethod(fix_events)

View File

@ -7,7 +7,7 @@ from typing import Any
async def run_in_thread(func) -> Any: async def run_in_thread(func) -> Any:
"""Run a function in a separate thread. """Run a function in a separate thread.
To not block the UI event queue, run_in_thread must be inside inside a rx.background() decorated method. To not block the UI event queue, run_in_thread must be inside inside a rx.event(background=True) decorated method.
Args: Args:
func (callable): The non-async function to run. func (callable): The non-async function to run.

View File

@ -6,6 +6,7 @@ from collections import defaultdict
from typing import Any, Dict, List from typing import Any, Dict, List
from reflex.config import get_config from reflex.config import get_config
from reflex.event import EventType
DECORATED_PAGES: Dict[str, List] = defaultdict(list) DECORATED_PAGES: Dict[str, List] = defaultdict(list)
@ -17,7 +18,7 @@ def page(
description: str | None = None, description: str | None = None,
meta: list[Any] | None = None, meta: list[Any] | None = None,
script_tags: list[Any] | None = None, script_tags: list[Any] | None = None,
on_load: Any | list[Any] | None = None, on_load: EventType[[]] | None = None,
): ):
"""Decorate a function as a page. """Decorate a function as a page.

View File

@ -91,7 +91,7 @@ from reflex.utils.exceptions import (
) )
from reflex.utils.exec import is_testing_env from reflex.utils.exec import is_testing_env
from reflex.utils.serializers import serializer from reflex.utils.serializers import serializer
from reflex.utils.types import get_origin, override from reflex.utils.types import _isinstance, get_origin, override
from reflex.vars import VarData from reflex.vars import VarData
if TYPE_CHECKING: if TYPE_CHECKING:
@ -105,6 +105,15 @@ var = computed_var
# If the state is this large, it's considered a performance issue. # If the state is this large, it's considered a performance issue.
TOO_LARGE_SERIALIZED_STATE = 100 * 1024 # 100kb TOO_LARGE_SERIALIZED_STATE = 100 * 1024 # 100kb
# Errors caught during pickling of state
HANDLED_PICKLE_ERRORS = (
pickle.PicklingError,
AttributeError,
IndexError,
TypeError,
ValueError,
)
def _no_chain_background_task( def _no_chain_background_task(
state_cls: Type["BaseState"], name: str, fn: Callable state_cls: Type["BaseState"], name: str, fn: Callable
@ -636,7 +645,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
def computed_var_func(state: Self): def computed_var_func(state: Self):
result = f(state) result = f(state)
if not isinstance(result, of_type): if not _isinstance(result, of_type):
console.warn( console.warn(
f"Inline ComputedVar {f} expected type {of_type}, got {type(result)}. " f"Inline ComputedVar {f} expected type {of_type}, got {type(result)}. "
"You can specify expected type with `of_type` argument." "You can specify expected type with `of_type` argument."
@ -1274,6 +1283,19 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
f"All state variables must be declared before they can be set." f"All state variables must be declared before they can be set."
) )
fields = self.get_fields()
if name in fields and not _isinstance(
value, (field_type := fields[name].outer_type_)
):
console.deprecate(
"mismatched-type-assignment",
f"Tried to assign value {value} of type {type(value)} to field {type(self).__name__}.{name} of type {field_type}."
" This might lead to unexpected behavior.",
"0.6.5",
"0.7.0",
)
# Set the attribute. # Set the attribute.
super().__setattr__(name, value) super().__setattr__(name, value)
@ -2063,7 +2085,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
""" """
try: try:
return pickle.dumps((self._to_schema(), self)) return pickle.dumps((self._to_schema(), self))
except (pickle.PicklingError, AttributeError) as og_pickle_error: except HANDLED_PICKLE_ERRORS as og_pickle_error:
error = ( error = (
f"Failed to serialize state {self.get_full_name()} due to unpicklable object. " f"Failed to serialize state {self.get_full_name()} due to unpicklable object. "
"This state will not be persisted. " "This state will not be persisted. "
@ -2077,7 +2099,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
f"Pickle error: {og_pickle_error}. " f"Pickle error: {og_pickle_error}. "
"Consider `pip install 'dill>=0.3.8'` for more exotic serialization support." "Consider `pip install 'dill>=0.3.8'` for more exotic serialization support."
) )
except (pickle.PicklingError, TypeError, ValueError) as ex: except HANDLED_PICKLE_ERRORS as ex:
error += f"Dill was also unable to pickle the state: {ex}" error += f"Dill was also unable to pickle the state: {ex}"
console.warn(error) console.warn(error)
return b"" return b""
@ -2346,7 +2368,7 @@ class StateProxy(wrapt.ObjectProxy):
class State(rx.State): class State(rx.State):
counter: int = 0 counter: int = 0
@rx.background @rx.event(background=True)
async def bg_increment(self): async def bg_increment(self):
await asyncio.sleep(1) await asyncio.sleep(1)
async with self: async with self:
@ -3248,7 +3270,7 @@ class StateManagerRedis(StateManager):
raise LockExpiredError( raise LockExpiredError(
f"Lock expired for token {token} while processing. Consider increasing " f"Lock expired for token {token} while processing. Consider increasing "
f"`app.state_manager.lock_expiration` (currently {self.lock_expiration}) " f"`app.state_manager.lock_expiration` (currently {self.lock_expiration}) "
"or use `@rx.background` decorator for long-running tasks." "or use `@rx.event(background=True)` decorator for long-running tasks."
) )
client_token, substate_name = _split_substate_key(token) client_token, substate_name = _split_substate_key(token)
# If the substate name on the token doesn't match the instance name, it cannot have a parent. # If the substate name on the token doesn't match the instance name, it cannot have a parent.

View File

@ -90,7 +90,11 @@ class MatchTypeError(ReflexError, TypeError):
class EventHandlerArgMismatch(ReflexError, TypeError): class EventHandlerArgMismatch(ReflexError, TypeError):
"""Raised when the number of args accepted by an EventHandler is differs from that provided by the event trigger.""" """Raised when the number of args accepted by an EventHandler differs from that provided by the event trigger."""
class EventHandlerArgTypeMismatch(ReflexError, TypeError):
"""Raised when the annotations of args accepted by an EventHandler differs from the spec of the event trigger."""
class EventFnArgMismatch(ReflexError, TypeError): class EventFnArgMismatch(ReflexError, TypeError):
@ -143,3 +147,7 @@ class EnvironmentVarValueError(ReflexError, ValueError):
class DynamicComponentInvalidSignature(ReflexError, TypeError): class DynamicComponentInvalidSignature(ReflexError, TypeError):
"""Raised when a dynamic component has an invalid signature.""" """Raised when a dynamic component has an invalid signature."""
class InvalidPropValueError(ReflexError):
"""Raised when a prop value is invalid."""

View File

@ -197,8 +197,16 @@ def make_default_page_title(app_name: str, route: str) -> str:
Returns: Returns:
The default page title. The default page title.
""" """
title = constants.DefaultPage.TITLE.format(app_name, route) route_parts = [
return to_title_case(title, " ") part
for part in route.split("/")
if part and not (part.startswith("[") and part.endswith("]"))
]
title = constants.DefaultPage.TITLE.format(
app_name, route_parts[-1] if route_parts else constants.PageNames.INDEX_ROUTE
)
return to_title_case(title)
def _escape_js_string(string: str) -> str: def _escape_js_string(string: str) -> str:

View File

@ -677,6 +677,7 @@ def _update_next_config(
"compress": config.next_compression, "compress": config.next_compression,
"reactStrictMode": config.react_strict_mode, "reactStrictMode": config.react_strict_mode,
"trailingSlash": True, "trailingSlash": True,
"staticPageGenerationTimeout": config.static_page_generation_timeout,
} }
if transpile_packages: if transpile_packages:
next_config["transpilePackages"] = list( next_config["transpilePackages"] = list(

View File

@ -490,7 +490,7 @@ def _generate_component_create_functiondef(
def figure_out_return_type(annotation: Any): def figure_out_return_type(annotation: Any):
if inspect.isclass(annotation) and issubclass(annotation, inspect._empty): if inspect.isclass(annotation) and issubclass(annotation, inspect._empty):
return ast.Name(id="Optional[EventType]") return ast.Name(id="EventType")
if not isinstance(annotation, str) and get_origin(annotation) is tuple: if not isinstance(annotation, str) and get_origin(annotation) is tuple:
arguments = get_args(annotation) arguments = get_args(annotation)
@ -509,20 +509,13 @@ def _generate_component_create_functiondef(
# Create EventType using the joined string # Create EventType using the joined string
event_type = ast.Name(id=f"EventType[{args_str}]") event_type = ast.Name(id=f"EventType[{args_str}]")
# Wrap in Optional return event_type
optional_type = ast.Subscript(
value=ast.Name(id="Optional"),
slice=ast.Index(value=event_type),
ctx=ast.Load(),
)
return ast.Name(id=ast.unparse(optional_type))
if isinstance(annotation, str) and annotation.startswith("Tuple["): if isinstance(annotation, str) and annotation.startswith("Tuple["):
inside_of_tuple = annotation.removeprefix("Tuple[").removesuffix("]") inside_of_tuple = annotation.removeprefix("Tuple[").removesuffix("]")
if inside_of_tuple == "()": if inside_of_tuple == "()":
return ast.Name(id="Optional[EventType[[]]]") return ast.Name(id="EventType[[]]")
arguments = [""] arguments = [""]
@ -548,10 +541,8 @@ def _generate_component_create_functiondef(
for argument in arguments for argument in arguments
] ]
return ast.Name( return ast.Name(id=f"EventType[{', '.join(arguments_without_var)}]")
id=f"Optional[EventType[{', '.join(arguments_without_var)}]]" return ast.Name(id="EventType")
)
return ast.Name(id="Optional[EventType]")
event_triggers = clz().get_event_triggers() event_triggers = clz().get_event_triggers()
@ -560,14 +551,40 @@ def _generate_component_create_functiondef(
( (
ast.arg( ast.arg(
arg=trigger, arg=trigger,
annotation=figure_out_return_type( annotation=ast.Subscript(
inspect.signature(event_triggers[trigger]).return_annotation ast.Name("Optional"),
ast.Index( # type: ignore
value=ast.Name(
id=ast.unparse(
figure_out_return_type(
inspect.signature(event_specs).return_annotation
)
if not isinstance(
event_specs := event_triggers[trigger], tuple
)
else ast.Subscript(
ast.Name("Union"),
ast.Tuple(
[
figure_out_return_type(
inspect.signature(
event_spec
).return_annotation
)
for event_spec in event_specs
]
),
)
)
)
),
), ),
), ),
ast.Constant(value=None), ast.Constant(value=None),
) )
for trigger in sorted(event_triggers) for trigger in sorted(event_triggers)
) )
logger.debug(f"Generated {clz.__name__}.create method with {len(kwargs)} kwargs") logger.debug(f"Generated {clz.__name__}.create method with {len(kwargs)} kwargs")
create_args = ast.arguments( create_args = ast.arguments(
args=[ast.arg(arg="cls")], args=[ast.arg(arg="cls")],
@ -578,12 +595,17 @@ def _generate_component_create_functiondef(
kwarg=ast.arg(arg="props"), kwarg=ast.arg(arg="props"),
defaults=[], defaults=[],
) )
definition = ast.FunctionDef( definition = ast.FunctionDef(
name="create", name="create",
args=create_args, args=create_args,
body=[ body=[
ast.Expr( ast.Expr(
value=ast.Constant(value=_generate_docstrings(all_classes, all_props)) value=ast.Constant(
value=_generate_docstrings(
all_classes, [*all_props, *event_triggers]
)
),
), ),
ast.Expr( ast.Expr(
value=ast.Ellipsis(), value=ast.Ellipsis(),

View File

@ -510,16 +510,66 @@ def _issubclass(cls: GenericType, cls_check: GenericType, instance: Any = None)
raise TypeError(f"Invalid type for issubclass: {cls_base}") from te raise TypeError(f"Invalid type for issubclass: {cls_base}") from te
def _isinstance(obj: Any, cls: GenericType) -> bool: def _isinstance(obj: Any, cls: GenericType, nested: bool = False) -> bool:
"""Check if an object is an instance of a class. """Check if an object is an instance of a class.
Args: Args:
obj: The object to check. obj: The object to check.
cls: The class to check against. cls: The class to check against.
nested: Whether the check is nested.
Returns: Returns:
Whether the object is an instance of the class. Whether the object is an instance of the class.
""" """
if cls is Any:
return True
if cls is None or cls is type(None):
return obj is None
if is_literal(cls):
return obj in get_args(cls)
if is_union(cls):
return any(_isinstance(obj, arg) for arg in get_args(cls))
origin = get_origin(cls)
if origin is None:
# cls is a simple class
return isinstance(obj, cls)
args = get_args(cls)
if not args:
# cls is a simple generic class
return isinstance(obj, origin)
if nested and args:
if origin is list:
return isinstance(obj, list) and all(
_isinstance(item, args[0]) for item in obj
)
if origin is tuple:
if args[-1] is Ellipsis:
return isinstance(obj, tuple) and all(
_isinstance(item, args[0]) for item in obj
)
return (
isinstance(obj, tuple)
and len(obj) == len(args)
and all(_isinstance(item, arg) for item, arg in zip(obj, args))
)
if origin is dict:
return isinstance(obj, dict) and all(
_isinstance(key, args[0]) and _isinstance(value, args[1])
for key, value in obj.items()
)
if origin is set:
return isinstance(obj, set) and all(
_isinstance(item, args[0]) for item in obj
)
return isinstance(obj, get_base_class(cls)) return isinstance(obj, get_base_class(cls))
@ -724,3 +774,69 @@ def validate_parameter_literals(func):
# Store this here for performance. # Store this here for performance.
StateBases = get_base_class(StateVar) StateBases = get_base_class(StateVar)
StateIterBases = get_base_class(StateIterVar) StateIterBases = get_base_class(StateIterVar)
def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> bool:
"""Check if a type hint is a subclass of another type hint.
Args:
possible_subclass: The type hint to check.
possible_superclass: The type hint to check against.
Returns:
Whether the type hint is a subclass of the other type hint.
"""
if possible_superclass is Any:
return True
if possible_subclass is Any:
return False
provided_type_origin = get_origin(possible_subclass)
accepted_type_origin = get_origin(possible_superclass)
if provided_type_origin is None and accepted_type_origin is None:
# In this case, we are dealing with a non-generic type, so we can use issubclass
return issubclass(possible_subclass, possible_superclass)
# Remove this check when Python 3.10 is the minimum supported version
if hasattr(types, "UnionType"):
provided_type_origin = (
Union if provided_type_origin is types.UnionType else provided_type_origin
)
accepted_type_origin = (
Union if accepted_type_origin is types.UnionType else accepted_type_origin
)
# Get type arguments (e.g., [float, int] for Dict[float, int])
provided_args = get_args(possible_subclass)
accepted_args = get_args(possible_superclass)
if accepted_type_origin is Union:
if provided_type_origin is not Union:
return any(
typehint_issubclass(possible_subclass, accepted_arg)
for accepted_arg in accepted_args
)
return all(
any(
typehint_issubclass(provided_arg, accepted_arg)
for accepted_arg in accepted_args
)
for provided_arg in provided_args
)
# Check if the origin of both types is the same (e.g., list for List[int])
# This probably should be issubclass instead of ==
if (provided_type_origin or possible_subclass) != (
accepted_type_origin or possible_superclass
):
return False
# Ensure all specific types are compatible with accepted types
# Note this is not necessarily correct, as it doesn't check against contravariance and covariance
# It also ignores when the length of the arguments is different
return all(
typehint_issubclass(provided_arg, accepted_arg)
for provided_arg, accepted_arg in zip(provided_args, accepted_args)
if accepted_arg is not Any
)

View File

@ -63,7 +63,14 @@ from reflex.utils.imports import (
ParsedImportDict, ParsedImportDict,
parse_imports, parse_imports,
) )
from reflex.utils.types import GenericType, Self, get_origin, has_args, unionize from reflex.utils.types import (
GenericType,
Self,
_isinstance,
get_origin,
has_args,
unionize,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from reflex.state import BaseState from reflex.state import BaseState
@ -612,10 +619,6 @@ class Var(Generic[VAR_TYPE]):
""" """
from .object import ObjectVar from .object import ObjectVar
base_type = var_type
if types.is_optional(base_type):
base_type = types.get_args(base_type)[0]
fixed_output_type = get_origin(output) or output fixed_output_type = get_origin(output) or output
# If the first argument is a python type, we map it to the corresponding Var type. # If the first argument is a python type, we map it to the corresponding Var type.
@ -1833,6 +1836,14 @@ class ComputedVar(Var[RETURN_TYPE]):
"return", Any "return", Any
) )
if hint is Any:
console.deprecate(
"untyped-computed-var",
"ComputedVar should have a return type annotation.",
"0.6.5",
"0.7.0",
)
kwargs.setdefault("_js_expr", fget.__name__) kwargs.setdefault("_js_expr", fget.__name__)
kwargs.setdefault("_var_type", hint) kwargs.setdefault("_var_type", hint)
@ -2026,8 +2037,8 @@ class ComputedVar(Var[RETURN_TYPE]):
) )
if not self._cache: if not self._cache:
return self.fget(instance) value = self.fget(instance)
else:
# handle caching # handle caching
if not hasattr(instance, self._cache_attr) or self.needs_update(instance): if not hasattr(instance, self._cache_attr) or self.needs_update(instance):
# Set cache attr on state instance. # Set cache attr on state instance.
@ -2036,7 +2047,18 @@ class ComputedVar(Var[RETURN_TYPE]):
instance._was_touched = True instance._was_touched = True
# Set the last updated timestamp on the state instance. # Set the last updated timestamp on the state instance.
setattr(instance, self._last_updated_attr, datetime.datetime.now()) setattr(instance, self._last_updated_attr, datetime.datetime.now())
return getattr(instance, self._cache_attr) value = getattr(instance, self._cache_attr)
if not _isinstance(value, self._var_type):
console.deprecate(
"mismatched-computed-var-return",
f"Computed var {type(instance).__name__}.{self._js_expr} returned value of type {type(value)}, "
f"expected {self._var_type}. This might cause unexpected behavior.",
"0.6.5",
"0.7.0",
)
return value
def _deps( def _deps(
self, self,

View File

@ -1,4 +1,4 @@
"""Test @rx.background task functionality.""" """Test @rx.event(background=True) task functionality."""
from typing import Generator from typing import Generator
@ -22,8 +22,7 @@ def BackgroundTask():
_task_id: int = 0 _task_id: int = 0
iterations: int = 10 iterations: int = 10
@rx.background @rx.event(background=True)
@rx.event
async def handle_event(self): async def handle_event(self):
async with self: async with self:
self._task_id += 1 self._task_id += 1
@ -32,8 +31,7 @@ def BackgroundTask():
self.counter += 1 self.counter += 1
await asyncio.sleep(0.005) await asyncio.sleep(0.005)
@rx.background @rx.event(background=True)
@rx.event
async def handle_event_yield_only(self): async def handle_event_yield_only(self):
async with self: async with self:
self._task_id += 1 self._task_id += 1
@ -48,7 +46,7 @@ def BackgroundTask():
def increment(self): def increment(self):
self.counter += 1 self.counter += 1
@rx.background @rx.event(background=True)
async def increment_arbitrary(self, amount: int): async def increment_arbitrary(self, amount: int):
async with self: async with self:
self.counter += int(amount) self.counter += int(amount)
@ -61,8 +59,7 @@ def BackgroundTask():
async def blocking_pause(self): async def blocking_pause(self):
await asyncio.sleep(0.02) await asyncio.sleep(0.02)
@rx.background @rx.event(background=True)
@rx.event
async def non_blocking_pause(self): async def non_blocking_pause(self):
await asyncio.sleep(0.02) await asyncio.sleep(0.02)
@ -74,15 +71,13 @@ def BackgroundTask():
self.counter += 1 self.counter += 1
await asyncio.sleep(0.005) await asyncio.sleep(0.005)
@rx.background @rx.event(background=True)
@rx.event
async def handle_racy_event(self): async def handle_racy_event(self):
await asyncio.gather( await asyncio.gather(
self.racy_task(), self.racy_task(), self.racy_task(), self.racy_task() self.racy_task(), self.racy_task(), self.racy_task(), self.racy_task()
) )
@rx.background @rx.event(background=True)
@rx.event
async def nested_async_with_self(self): async def nested_async_with_self(self):
async with self: async with self:
self.counter += 1 self.counter += 1
@ -94,8 +89,7 @@ def BackgroundTask():
third_state = await self.get_state(ThirdState) third_state = await self.get_state(ThirdState)
await third_state._triple_count() await third_state._triple_count()
@rx.background @rx.event(background=True)
@rx.event
async def yield_in_async_with_self(self): async def yield_in_async_with_self(self):
async with self: async with self:
self.counter += 1 self.counter += 1
@ -103,8 +97,7 @@ def BackgroundTask():
self.counter += 1 self.counter += 1
class OtherState(rx.State): class OtherState(rx.State):
@rx.background @rx.event(background=True)
@rx.event
async def get_other_state(self): async def get_other_state(self):
async with self: async with self:
state = await self.get_state(State) state = await self.get_state(State)

View File

@ -121,7 +121,7 @@ def FormSubmitName(form_component):
on_change=rx.console_log, on_change=rx.console_log,
), ),
rx.button("Submit", type_="submit"), rx.button("Submit", type_="submit"),
rx.icon_button(FormState.val, icon=rx.icon(tag="plus")), rx.icon_button(rx.icon(tag="plus")),
), ),
on_submit=FormState.form_submit, on_submit=FormState.form_submit,
custom_attrs={"action": "/invalid"}, custom_attrs={"action": "/invalid"},

View File

@ -793,8 +793,8 @@ def test_var_operations(driver, var_operations: AppHarness):
("foreach_list_ix", "1\n2"), ("foreach_list_ix", "1\n2"),
("foreach_list_nested", "1\n1\n2"), ("foreach_list_nested", "1\n1\n2"),
# rx.memo component with state # rx.memo component with state
("memo_comp", "[1,2]10"), ("memo_comp", "1210"),
("memo_comp_nested", "[3,4]5"), ("memo_comp_nested", "345"),
# foreach in a match # foreach in a match
("foreach_in_match", "first\nsecond\nthird"), ("foreach_in_match", "first\nsecond\nthird"),
] ]

View File

@ -1,205 +0,0 @@
import pytest
import reflex as rx
@pytest.fixture
def upload_root_component():
"""A test upload component function.
Returns:
A test upload component function.
"""
def upload_root_component():
return rx.upload.root(
rx.button("select file"),
rx.text("Drag and drop files here or click to select files"),
border="1px dotted black",
)
return upload_root_component()
@pytest.fixture
def upload_component():
"""A test upload component function.
Returns:
A test upload component function.
"""
def upload_component():
return rx.upload(
rx.button("select file"),
rx.text("Drag and drop files here or click to select files"),
border="1px dotted black",
)
return upload_component()
@pytest.fixture
def upload_component_id_special():
def upload_component():
return rx.upload(
rx.button("select file"),
rx.text("Drag and drop files here or click to select files"),
border="1px dotted black",
id="#spec!`al-_98ID",
)
return upload_component()
@pytest.fixture
def upload_component_with_props():
"""A test upload component with props function.
Returns:
A test upload component with props function.
"""
def upload_component_with_props():
return rx.upload(
rx.button("select file"),
rx.text("Drag and drop files here or click to select files"),
border="1px dotted black",
no_drag=True,
max_files=2,
)
return upload_component_with_props()
def test_upload_root_component_render(upload_root_component):
"""Test that the render function is set correctly.
Args:
upload_root_component: component fixture
"""
upload = upload_root_component.render()
# upload
assert upload["name"] == "ReactDropzone"
assert upload["props"] == [
'id={"default"}',
"multiple={true}",
"onDrop={e => setFilesById(filesById => {\n"
" const updatedFilesById = Object.assign({}, filesById);\n"
' updatedFilesById["default"] = e;\n'
" return updatedFilesById;\n"
" })\n"
" }",
"ref={ref_default}",
]
assert upload["args"] == ("getRootProps", "getInputProps")
# box inside of upload
[box] = upload["children"]
assert box["name"] == "RadixThemesBox"
assert box["props"] == [
'className={"rx-Upload"}',
'css={({ ["border"] : "1px dotted black" })}',
"{...getRootProps()}",
]
# input, button and text inside of box
[input, button, text] = box["children"]
assert input["name"] == "input"
assert input["props"] == ['type={"file"}', "{...getInputProps()}"]
assert button["name"] == "RadixThemesButton"
assert button["children"][0]["contents"] == '{"select file"}'
assert text["name"] == "RadixThemesText"
assert (
text["children"][0]["contents"]
== '{"Drag and drop files here or click to select files"}'
)
def test_upload_component_render(upload_component):
"""Test that the render function is set correctly.
Args:
upload_component: component fixture
"""
upload = upload_component.render()
# upload
assert upload["name"] == "ReactDropzone"
assert upload["props"] == [
'id={"default"}',
"multiple={true}",
"onDrop={e => setFilesById(filesById => {\n"
" const updatedFilesById = Object.assign({}, filesById);\n"
' updatedFilesById["default"] = e;\n'
" return updatedFilesById;\n"
" })\n"
" }",
"ref={ref_default}",
]
assert upload["args"] == ("getRootProps", "getInputProps")
# box inside of upload
[box] = upload["children"]
assert box["name"] == "RadixThemesBox"
assert box["props"] == [
'className={"rx-Upload"}',
'css={({ ["border"] : "1px dotted black", ["padding"] : "5em", ["textAlign"] : "center" })}',
"{...getRootProps()}",
]
# input, button and text inside of box
[input, button, text] = box["children"]
assert input["name"] == "input"
assert input["props"] == ['type={"file"}', "{...getInputProps()}"]
assert button["name"] == "RadixThemesButton"
assert button["children"][0]["contents"] == '{"select file"}'
assert text["name"] == "RadixThemesText"
assert (
text["children"][0]["contents"]
== '{"Drag and drop files here or click to select files"}'
)
def test_upload_component_with_props_render(upload_component_with_props):
"""Test that the render function is set correctly.
Args:
upload_component_with_props: component fixture
"""
upload = upload_component_with_props.render()
assert upload["props"] == [
'id={"default"}',
"maxFiles={2}",
"multiple={true}",
"noDrag={true}",
"onDrop={e => setFilesById(filesById => {\n"
" const updatedFilesById = Object.assign({}, filesById);\n"
' updatedFilesById["default"] = e;\n'
" return updatedFilesById;\n"
" })\n"
" }",
"ref={ref_default}",
]
def test_upload_component_id_with_special_chars(upload_component_id_special):
upload = upload_component_id_special.render()
assert upload["props"] == [
r'id={"#spec!`al-_98ID"}',
"multiple={true}",
"onDrop={e => setFilesById(filesById => {\n"
" const updatedFilesById = Object.assign({}, filesById);\n"
' updatedFilesById["#spec!`al-_98ID"] = e;\n'
" return updatedFilesById;\n"
" })\n"
" }",
"ref={ref__spec_al__98ID}",
]

View File

@ -20,13 +20,17 @@ from reflex.event import (
EventChain, EventChain,
EventHandler, EventHandler,
empty_event, empty_event,
identity_event,
input_event, input_event,
parse_args_spec, parse_args_spec,
) )
from reflex.state import BaseState from reflex.state import BaseState
from reflex.style import Style from reflex.style import Style
from reflex.utils import imports from reflex.utils import imports
from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch from reflex.utils.exceptions import (
EventFnArgMismatch,
EventHandlerArgMismatch,
)
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
from reflex.vars import VarData from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var from reflex.vars.base import LiteralVar, Var
@ -43,6 +47,18 @@ def test_state():
def do_something_arg(self, arg): def do_something_arg(self, arg):
pass pass
def do_something_with_bool(self, arg: bool):
pass
def do_something_with_int(self, arg: int):
pass
def do_something_with_list_int(self, arg: list[int]):
pass
def do_something_with_list_str(self, arg: list[str]):
pass
return TestState return TestState
@ -95,8 +111,10 @@ def component2() -> Type[Component]:
""" """
return { return {
**super().get_event_triggers(), **super().get_event_triggers(),
"on_open": lambda e0: [e0], "on_open": identity_event(bool),
"on_close": lambda e0: [e0], "on_close": identity_event(bool),
"on_user_visited_count_changed": identity_event(int),
"on_user_list_changed": identity_event(List[str]),
} }
def _get_imports(self) -> ParsedImportDict: def _get_imports(self) -> ParsedImportDict:
@ -582,7 +600,14 @@ def test_get_event_triggers(component1, component2):
assert component1().get_event_triggers().keys() == default_triggers assert component1().get_event_triggers().keys() == default_triggers
assert ( assert (
component2().get_event_triggers().keys() component2().get_event_triggers().keys()
== {"on_open", "on_close", "on_prop_event"} | default_triggers == {
"on_open",
"on_close",
"on_prop_event",
"on_user_visited_count_changed",
"on_user_list_changed",
}
| default_triggers
) )
@ -642,21 +667,18 @@ def test_component_create_unallowed_types(children, test_component):
"name": "Fragment", "name": "Fragment",
"props": [], "props": [],
"contents": "", "contents": "",
"args": None,
"special_props": [], "special_props": [],
"children": [ "children": [
{ {
"name": "RadixThemesText", "name": "RadixThemesText",
"props": ['as={"p"}'], "props": ['as={"p"}'],
"contents": "", "contents": "",
"args": None,
"special_props": [], "special_props": [],
"children": [ "children": [
{ {
"name": "", "name": "",
"props": [], "props": [],
"contents": '{"first_text"}', "contents": '{"first_text"}',
"args": None,
"special_props": [], "special_props": [],
"children": [], "children": [],
"autofocus": False, "autofocus": False,
@ -671,15 +693,12 @@ def test_component_create_unallowed_types(children, test_component):
( (
(rx.text("first_text"), rx.text("second_text")), (rx.text("first_text"), rx.text("second_text")),
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [ "children": [
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [ "children": [
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [], "children": [],
"contents": '{"first_text"}', "contents": '{"first_text"}',
@ -694,11 +713,9 @@ def test_component_create_unallowed_types(children, test_component):
"special_props": [], "special_props": [],
}, },
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [ "children": [
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [], "children": [],
"contents": '{"second_text"}', "contents": '{"second_text"}',
@ -722,15 +739,12 @@ def test_component_create_unallowed_types(children, test_component):
( (
(rx.text("first_text"), rx.box((rx.text("second_text"),))), (rx.text("first_text"), rx.box((rx.text("second_text"),))),
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [ "children": [
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [ "children": [
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [], "children": [],
"contents": '{"first_text"}', "contents": '{"first_text"}',
@ -745,19 +759,15 @@ def test_component_create_unallowed_types(children, test_component):
"special_props": [], "special_props": [],
}, },
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [ "children": [
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [ "children": [
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [ "children": [
{ {
"args": None,
"autofocus": False, "autofocus": False,
"children": [], "children": [],
"contents": '{"second_text"}', "contents": '{"second_text"}',
@ -918,6 +928,22 @@ def test_invalid_event_handler_args(component2, test_state):
on_prop_event=[test_state.do_something_arg, test_state.do_something] on_prop_event=[test_state.do_something_arg, test_state.do_something]
) )
# Enable when 0.7.0 happens
# # Event Handler types must match
# with pytest.raises(EventHandlerArgTypeMismatch):
# component2.create(
# on_user_visited_count_changed=test_state.do_something_with_bool
# )
# with pytest.raises(EventHandlerArgTypeMismatch):
# component2.create(on_user_list_changed=test_state.do_something_with_int)
# with pytest.raises(EventHandlerArgTypeMismatch):
# component2.create(on_user_list_changed=test_state.do_something_with_list_int)
# component2.create(on_open=test_state.do_something_with_int)
# component2.create(on_open=test_state.do_something_with_bool)
# component2.create(on_user_visited_count_changed=test_state.do_something_with_int)
# component2.create(on_user_list_changed=test_state.do_something_with_list_str)
# lambda cannot return weird values. # lambda cannot return weird values.
with pytest.raises(ValueError): with pytest.raises(ValueError):
component2.create(on_click=lambda: 1) component2.create(on_click=lambda: 1)
@ -1117,10 +1143,10 @@ def test_component_with_only_valid_children(fixture, request):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"component,rendered", "component,rendered",
[ [
(rx.text("hi"), '<RadixThemesText as={"p"}>\n {"hi"}\n</RadixThemesText>'), (rx.text("hi"), '<RadixThemesText as={"p"}>\n\n{"hi"}\n</RadixThemesText>'),
( (
rx.box(rx.heading("test", size="3")), rx.box(rx.heading("test", size="3")),
'<RadixThemesBox>\n <RadixThemesHeading size={"3"}>\n {"test"}\n</RadixThemesHeading>\n</RadixThemesBox>', '<RadixThemesBox>\n\n<RadixThemesHeading size={"3"}>\n\n{"test"}\n</RadixThemesHeading>\n</RadixThemesBox>',
), ),
], ],
) )

View File

@ -0,0 +1,63 @@
import pytest
from reflex.components.props import NoExtrasAllowedProps
from reflex.utils.exceptions import InvalidPropValueError
try:
from pydantic.v1 import ValidationError
except ModuleNotFoundError:
from pydantic import ValidationError
class PropA(NoExtrasAllowedProps):
"""Base prop class."""
foo: str
bar: str
class PropB(NoExtrasAllowedProps):
"""Prop class with nested props."""
foobar: str
foobaz: PropA
@pytest.mark.parametrize(
"props_class, kwargs, should_raise",
[
(PropA, {"foo": "value", "bar": "another_value"}, False),
(PropA, {"fooz": "value", "bar": "another_value"}, True),
(
PropB,
{
"foobaz": {"foo": "value", "bar": "another_value"},
"foobar": "foo_bar_value",
},
False,
),
(
PropB,
{
"fooba": {"foo": "value", "bar": "another_value"},
"foobar": "foo_bar_value",
},
True,
),
(
PropB,
{
"foobaz": {"foobar": "value", "bar": "another_value"},
"foobar": "foo_bar_value",
},
True,
),
],
)
def test_no_extras_allowed_props(props_class, kwargs, should_raise):
if should_raise:
with pytest.raises((ValidationError, InvalidPropValueError)):
props_class(**kwargs)
else:
props_instance = props_class(**kwargs)
assert isinstance(props_instance, props_class)

View File

@ -71,7 +71,7 @@ class FileUploadState(State):
assert file.filename is not None assert file.filename is not None
self.img_list.append(file.filename) self.img_list.append(file.filename)
@rx.background @rx.event(background=True)
async def bg_upload(self, files: List[rx.UploadFile]): async def bg_upload(self, files: List[rx.UploadFile]):
"""Background task cannot be upload handler. """Background task cannot be upload handler.
@ -119,7 +119,7 @@ class ChildFileUploadState(FileStateBase1):
assert file.filename is not None assert file.filename is not None
self.img_list.append(file.filename) self.img_list.append(file.filename)
@rx.background @rx.event(background=True)
async def bg_upload(self, files: List[rx.UploadFile]): async def bg_upload(self, files: List[rx.UploadFile]):
"""Background task cannot be upload handler. """Background task cannot be upload handler.
@ -167,7 +167,7 @@ class GrandChildFileUploadState(FileStateBase2):
assert file.filename is not None assert file.filename is not None
self.img_list.append(file.filename) self.img_list.append(file.filename)
@rx.background @rx.event(background=True)
async def bg_upload(self, files: List[rx.UploadFile]): async def bg_upload(self, files: List[rx.UploadFile]):
"""Background task cannot be upload handler. """Background task cannot be upload handler.

View File

@ -874,7 +874,7 @@ async def test_upload_file_background(state, tmp_path, token):
await fn(request_mock, [file_mock]) await fn(request_mock, [file_mock])
assert ( assert (
err.value.args[0] err.value.args[0]
== f"@rx.background is not supported for upload handler `{state.get_full_name()}.bg_upload`." == f"@rx.event(background=True) is not supported for upload handler `{state.get_full_name()}.bg_upload`."
) )
if isinstance(app.state_manager, StateManagerRedis): if isinstance(app.state_manager, StateManagerRedis):
@ -1211,7 +1211,7 @@ async def test_process_events(mocker, token: str):
], ],
) )
def test_overlay_component( def test_overlay_component(
state: State | None, state: Type[State] | None,
overlay_component: Component | ComponentCallable | None, overlay_component: Component | ComponentCallable | None,
exp_page_child: Type[Component] | None, exp_page_child: Type[Component] | None,
): ):
@ -1403,13 +1403,6 @@ def test_app_state_determination():
assert a4.state is not None assert a4.state is not None
# for coverage
def test_raise_on_connect_error():
"""Test that the connect_error function is called."""
with pytest.raises(ValueError):
App(connect_error_component="Foo")
def test_raise_on_state(): def test_raise_on_state():
"""Test that the state is set.""" """Test that the state is set."""
# state kwargs is deprecated, we just make sure the app is created anyway. # state kwargs is deprecated, we just make sure the app is created anyway.

View File

@ -24,7 +24,15 @@ from reflex.utils.prerequisites import (
app_name="test", app_name="test",
), ),
False, False,
'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true};', 'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 60};',
),
(
Config(
app_name="test",
static_page_generation_timeout=30,
),
False,
'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 30};',
), ),
( (
Config( Config(
@ -32,7 +40,7 @@ from reflex.utils.prerequisites import (
next_compression=False, next_compression=False,
), ),
False, False,
'module.exports = {basePath: "", compress: false, reactStrictMode: true, trailingSlash: true};', 'module.exports = {basePath: "", compress: false, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 60};',
), ),
( (
Config( Config(
@ -40,7 +48,7 @@ from reflex.utils.prerequisites import (
frontend_path="/test", frontend_path="/test",
), ),
False, False,
'module.exports = {basePath: "/test", compress: true, reactStrictMode: true, trailingSlash: true};', 'module.exports = {basePath: "/test", compress: true, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 60};',
), ),
( (
Config( Config(
@ -49,14 +57,14 @@ from reflex.utils.prerequisites import (
next_compression=False, next_compression=False,
), ),
False, False,
'module.exports = {basePath: "/test", compress: false, reactStrictMode: true, trailingSlash: true};', 'module.exports = {basePath: "/test", compress: false, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 60};',
), ),
( (
Config( Config(
app_name="test", app_name="test",
), ),
True, True,
'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true, output: "export", distDir: "_static"};', 'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true, staticPageGenerationTimeout: 60, output: "export", distDir: "_static"};',
), ),
], ],
) )

View File

@ -8,6 +8,7 @@ import functools
import json import json
import os import os
import sys import sys
import threading
from textwrap import dedent from textwrap import dedent
from typing import Any, AsyncGenerator, Callable, Dict, List, Optional, Union from typing import Any, AsyncGenerator, Callable, Dict, List, Optional, Union
from unittest.mock import AsyncMock, Mock from unittest.mock import AsyncMock, Mock
@ -1965,7 +1966,7 @@ class BackgroundTaskState(BaseState):
""" """
return self.order return self.order
@rx.background @rx.event(background=True)
async def background_task(self): async def background_task(self):
"""A background task that updates the state.""" """A background task that updates the state."""
async with self: async with self:
@ -2002,7 +2003,7 @@ class BackgroundTaskState(BaseState):
self.other() # direct calling event handlers works in context self.other() # direct calling event handlers works in context
self._private_method() self._private_method()
@rx.background @rx.event(background=True)
async def background_task_reset(self): async def background_task_reset(self):
"""A background task that resets the state.""" """A background task that resets the state."""
with pytest.raises(ImmutableStateError): with pytest.raises(ImmutableStateError):
@ -2016,7 +2017,7 @@ class BackgroundTaskState(BaseState):
async with self: async with self:
self.order.append("reset") self.order.append("reset")
@rx.background @rx.event(background=True)
async def background_task_generator(self): async def background_task_generator(self):
"""A background task generator that does nothing. """A background task generator that does nothing.
@ -2724,6 +2725,7 @@ class OnLoadState(State):
num: int = 0 num: int = 0
@rx.event
def test_handler(self): def test_handler(self):
"""Test handler.""" """Test handler."""
self.num += 1 self.num += 1
@ -3390,9 +3392,15 @@ def test_fallback_pickle():
assert unpickled_state._f() == 420 assert unpickled_state._f() == 420
assert unpickled_state._o._f() == 42 assert unpickled_state._o._f() == 42
# Threading locks are unpicklable normally, and raise TypeError instead of PicklingError.
state2 = DillState(_reflex_internal_init=True) # type: ignore
state2._g = threading.Lock()
pk2 = state2._serialize()
unpickled_state2 = BaseState._deserialize(pk2)
assert isinstance(unpickled_state2._g, type(threading.Lock()))
# Some object, like generator, are still unpicklable with dill. # Some object, like generator, are still unpicklable with dill.
state._g = (i for i in range(10)) state3 = DillState(_reflex_internal_init=True) # type: ignore
pk = state._serialize() state3._g = (i for i in range(10))
assert len(pk) == 0 pk3 = state3._serialize()
with pytest.raises(EOFError): assert len(pk3) == 0
BaseState._deserialize(pk)

View File

@ -2,7 +2,7 @@ import os
import typing import typing
from functools import cached_property from functools import cached_property
from pathlib import Path from pathlib import Path
from typing import Any, ClassVar, List, Literal, Type, Union from typing import Any, ClassVar, Dict, List, Literal, Type, Union
import pytest import pytest
import typer import typer
@ -77,6 +77,47 @@ def test_is_generic_alias(cls: type, expected: bool):
assert types.is_generic_alias(cls) == expected assert types.is_generic_alias(cls) == expected
@pytest.mark.parametrize(
("subclass", "superclass", "expected"),
[
*[
(base_type, base_type, True)
for base_type in [int, float, str, bool, list, dict]
],
*[
(one_type, another_type, False)
for one_type in [int, float, str, list, dict]
for another_type in [int, float, str, list, dict]
if one_type != another_type
],
(bool, int, True),
(int, bool, False),
(list, List, True),
(list, List[str], True), # this is wrong, but it's a limitation of the function
(List, list, True),
(List[int], list, True),
(List[int], List, True),
(List[int], List[str], False),
(List[int], List[int], True),
(List[int], List[float], False),
(List[int], List[Union[int, float]], True),
(List[int], List[Union[float, str]], False),
(Union[int, float], List[Union[int, float]], False),
(Union[int, float], Union[int, float, str], True),
(Union[int, float], Union[str, float], False),
(Dict[str, int], Dict[str, int], True),
(Dict[str, bool], Dict[str, int], True),
(Dict[str, int], Dict[str, bool], False),
(Dict[str, Any], dict[str, str], False),
(Dict[str, str], dict[str, str], True),
(Dict[str, str], dict[str, Any], True),
(Dict[str, Any], dict[str, Any], True),
],
)
def test_typehint_issubclass(subclass, superclass, expected):
assert types.typehint_issubclass(subclass, superclass) == expected
def test_validate_invalid_bun_path(mocker): def test_validate_invalid_bun_path(mocker):
"""Test that an error is thrown when a custom specified bun path is not valid """Test that an error is thrown when a custom specified bun path is not valid
or does not exist. or does not exist.