diff --git a/reflex/app.py b/reflex/app.py index a3d0d8e10..7b7010521 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -99,7 +99,15 @@ from reflex.state import ( _substate_key, code_uses_state_contexts, ) -from reflex.utils import codespaces, console, exceptions, format, prerequisites, types +from reflex.utils import ( + codespaces, + console, + exceptions, + format, + path_ops, + prerequisites, + types, +) from reflex.utils.exec import is_prod_mode, is_testing_env from reflex.utils.imports import ImportVar @@ -991,9 +999,10 @@ class App(MiddlewareMixin, LifespanMixin): should_compile = self._should_compile() if not should_compile: - for route in self._unevaluated_pages: - console.debug(f"Evaluating page: {route}") - self._compile_page(route, save_page=should_compile) + with console.timing("Evaluate Pages (Backend)"): + for route in self._unevaluated_pages: + console.debug(f"Evaluating page: {route}") + self._compile_page(route, save_page=should_compile) # Add the optional endpoints (_upload) self._add_optional_endpoints() @@ -1019,10 +1028,11 @@ class App(MiddlewareMixin, LifespanMixin): + adhoc_steps_without_executor, ) - for route in self._unevaluated_pages: - console.debug(f"Evaluating page: {route}") - self._compile_page(route, save_page=should_compile) - progress.advance(task) + with console.timing("Evaluate Pages (Frontend)"): + for route in self._unevaluated_pages: + console.debug(f"Evaluating page: {route}") + self._compile_page(route, save_page=should_compile) + progress.advance(task) # Add the optional endpoints (_upload) self._add_optional_endpoints() @@ -1030,7 +1040,7 @@ class App(MiddlewareMixin, LifespanMixin): self._validate_var_dependencies() self._setup_overlay_component() self._setup_error_boundary() - if config.show_built_with_reflex: + if is_prod_mode() and config.show_built_with_reflex: self._setup_sticky_badge() progress.advance(task) @@ -1057,13 +1067,13 @@ class App(MiddlewareMixin, LifespanMixin): custom_components |= component._get_all_custom_components() # Perform auto-memoization of stateful components. - ( - stateful_components_path, - stateful_components_code, - page_components, - ) = compiler.compile_stateful_components(self._pages.values()) - - progress.advance(task) + with console.timing("Auto-memoize StatefulComponents"): + ( + stateful_components_path, + stateful_components_code, + page_components, + ) = compiler.compile_stateful_components(self._pages.values()) + progress.advance(task) # Catch "static" apps (that do not define a rx.State subclass) which are trying to access rx.State. if code_uses_state_contexts(stateful_components_code) and self._state is None: @@ -1086,6 +1096,17 @@ class App(MiddlewareMixin, LifespanMixin): progress.advance(task) + # Copy the assets. + assets_src = Path.cwd() / constants.Dirs.APP_ASSETS + if assets_src.is_dir(): + with console.timing("Copy assets"): + path_ops.update_directory_tree( + src=assets_src, + dest=( + Path.cwd() / prerequisites.get_web_dir() / constants.Dirs.PUBLIC + ), + ) + # Use a forking process pool, if possible. Much faster, especially for large sites. # Fallback to ThreadPoolExecutor as something that will always work. executor = None @@ -1138,9 +1159,10 @@ class App(MiddlewareMixin, LifespanMixin): _submit_work(compiler.remove_tailwind_from_postcss) # Wait for all compilation tasks to complete. - for future in concurrent.futures.as_completed(result_futures): - compile_results.append(future.result()) - progress.advance(task) + with console.timing("Compile to Javascript"): + for future in concurrent.futures.as_completed(result_futures): + compile_results.append(future.result()) + progress.advance(task) app_root = self._app_root(app_wrappers=app_wrappers) @@ -1175,7 +1197,8 @@ class App(MiddlewareMixin, LifespanMixin): progress.stop() # Install frontend packages. - self._get_frontend_packages(all_imports) + with console.timing("Install Frontend Packages"): + self._get_frontend_packages(all_imports) # Setup the next.config.js transpile_packages = [ @@ -1201,8 +1224,9 @@ class App(MiddlewareMixin, LifespanMixin): # Remove pages that are no longer in the app. p.unlink() - for output_path, code in compile_results: - compiler_utils.write_page(output_path, code) + with console.timing("Write to Disk"): + for output_path, code in compile_results: + compiler_utils.write_page(output_path, code) @contextlib.asynccontextmanager async def modify_state(self, token: str) -> AsyncIterator[BaseState]: diff --git a/reflex/compiler/utils.py b/reflex/compiler/utils.py index c797a095f..91ee18b86 100644 --- a/reflex/compiler/utils.py +++ b/reflex/compiler/utils.py @@ -119,24 +119,34 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]: validate_imports(collapsed_import_dict) import_dicts = [] for lib, fields in collapsed_import_dict.items(): - default, rest = compile_import_statement(fields) - # prevent lib from being rendered on the page if all imports are non rendered kind if not any(f.render for f in fields): continue - if not lib: - if default: - raise ValueError("No default field allowed for empty library.") - if rest is None or len(rest) == 0: - raise ValueError("No fields to import.") - import_dicts.extend(get_import_dict(module) for module in sorted(rest)) - continue + lib_paths: dict[str, list[ImportVar]] = {} - # remove the version before rendering the package imports - lib = format.format_library_name(lib) + for field in fields: + lib_paths.setdefault(field.package_path, []).append(field) - import_dicts.append(get_import_dict(lib, default, rest)) + compiled = { + path: compile_import_statement(fields) for path, fields in lib_paths.items() + } + + for path, (default, rest) in compiled.items(): + if not lib: + if default: + raise ValueError("No default field allowed for empty library.") + if rest is None or len(rest) == 0: + raise ValueError("No fields to import.") + import_dicts.extend(get_import_dict(module) for module in sorted(rest)) + continue + + # remove the version before rendering the package imports + formatted_lib = format.format_library_name(lib) + ( + path if path != "/" else "" + ) + + import_dicts.append(get_import_dict(formatted_lib, default, rest)) return import_dicts diff --git a/reflex/components/core/sticky.py b/reflex/components/core/sticky.py index 162bab3cd..b5dd4bcfd 100644 --- a/reflex/components/core/sticky.py +++ b/reflex/components/core/sticky.py @@ -2,14 +2,12 @@ from reflex.components.component import ComponentNamespace from reflex.components.core.colors import color -from reflex.components.core.cond import color_mode_cond, cond -from reflex.components.core.responsive import tablet_and_desktop +from reflex.components.core.cond import color_mode_cond +from reflex.components.core.responsive import desktop_only from reflex.components.el.elements.inline import A from reflex.components.el.elements.media import Path, Rect, Svg from reflex.components.radix.themes.typography.text import Text -from reflex.experimental.client_state import ClientStateVar from reflex.style import Style -from reflex.vars.base import Var, VarData class StickyLogo(Svg): @@ -87,7 +85,7 @@ class StickyBadge(A): """ return super().create( StickyLogo.create(), - tablet_and_desktop(StickyLabel.create()), + desktop_only(StickyLabel.create()), href="https://reflex.dev", target="_blank", width="auto", @@ -102,36 +100,12 @@ class StickyBadge(A): Returns: The style of the component. """ - is_localhost_cs = ClientStateVar.create( - "is_localhost", - default=True, - global_ref=False, - ) - localhost_hostnames = Var.create( - ["localhost", "127.0.0.1", "[::1]"] - ).guess_type() - is_localhost_expr = localhost_hostnames.contains( - Var("window.location.hostname", _var_type=str).guess_type(), - ) - check_is_localhost = Var( - f"useEffect(({is_localhost_cs}) => {is_localhost_cs.set}({is_localhost_expr}), [])", - _var_data=VarData( - imports={"react": "useEffect"}, - ), - ) - is_localhost = is_localhost_cs.value._replace( - merge_var_data=VarData.merge( - check_is_localhost._get_all_var_data(), - VarData(hooks={str(check_is_localhost): None}), - ), - ) return Style( { "position": "fixed", "bottom": "1rem", "right": "1rem", - # Do not show the badge on localhost. - "display": cond(is_localhost, "none", "flex"), + "display": "flex", "flex-direction": "row", "gap": "0.375rem", "align-items": "center", diff --git a/reflex/components/lucide/icon.py b/reflex/components/lucide/icon.py index 6c7cbede7..269ef7f79 100644 --- a/reflex/components/lucide/icon.py +++ b/reflex/components/lucide/icon.py @@ -4,7 +4,7 @@ from reflex.components.component import Component from reflex.utils import format from reflex.utils.imports import ImportVar from reflex.vars.base import LiteralVar, Var -from reflex.vars.sequence import LiteralStringVar +from reflex.vars.sequence import LiteralStringVar, StringVar class LucideIconComponent(Component): @@ -40,7 +40,12 @@ class Icon(LucideIconComponent): The created component. """ if children: - if len(children) == 1 and isinstance(children[0], str): + if len(children) == 1: + child = Var.create(children[0]).guess_type() + if not isinstance(child, StringVar): + raise AttributeError( + f"Icon name must be a string, got {children[0]._var_type if isinstance(children[0], Var) else children[0]}" + ) props["tag"] = children[0] else: raise AttributeError( @@ -56,7 +61,10 @@ class Icon(LucideIconComponent): else: raise TypeError(f"Icon name must be a string, got {type(tag)}") elif isinstance(tag, Var): - return DynamicIcon.create(name=tag, **props) + tag_stringified = tag.guess_type() + if not isinstance(tag_stringified, StringVar): + raise TypeError(f"Icon name must be a string, got {tag._var_type}") + return DynamicIcon.create(name=tag_stringified.replace("_", "-"), **props) if ( not isinstance(tag, str) diff --git a/reflex/components/plotly/__init__.py b/reflex/components/plotly/__init__.py index 5620d5fc4..8743b31b2 100644 --- a/reflex/components/plotly/__init__.py +++ b/reflex/components/plotly/__init__.py @@ -1,5 +1,32 @@ """Plotly components.""" -from .plotly import Plotly +from reflex.components.component import ComponentNamespace -plotly = Plotly.create +from .plotly import ( + Plotly, + PlotlyBasic, + PlotlyCartesian, + PlotlyFinance, + PlotlyGeo, + PlotlyGl2d, + PlotlyGl3d, + PlotlyMapbox, + PlotlyStrict, +) + + +class PlotlyNamespace(ComponentNamespace): + """Plotly namespace.""" + + __call__ = Plotly.create + basic = PlotlyBasic.create + cartesian = PlotlyCartesian.create + geo = PlotlyGeo.create + gl2d = PlotlyGl2d.create + gl3d = PlotlyGl3d.create + finance = PlotlyFinance.create + mapbox = PlotlyMapbox.create + strict = PlotlyStrict.create + + +plotly = PlotlyNamespace() diff --git a/reflex/components/plotly/plotly.py b/reflex/components/plotly/plotly.py index c85423d35..2ddaad8d7 100644 --- a/reflex/components/plotly/plotly.py +++ b/reflex/components/plotly/plotly.py @@ -10,6 +10,7 @@ from reflex.components.component import Component, NoSSRComponent from reflex.components.core.cond import color_mode_cond from reflex.event import EventHandler, no_args_event_spec from reflex.utils import console +from reflex.utils.imports import ImportDict, ImportVar from reflex.vars.base import LiteralVar, Var try: @@ -278,3 +279,237 @@ const extractPoints = (points) => { # Spread the figure dict over props, nothing to merge. tag.special_props.append(Var(_js_expr=f"{{...{figure!s}}}")) return tag + + +CREATE_PLOTLY_COMPONENT: ImportDict = { + "react-plotly.js": [ + ImportVar( + tag="createPlotlyComponent", + is_default=True, + package_path="/factory", + ), + ] +} + + +def dynamic_plotly_import(name: str, package: str) -> str: + """Create a dynamic import for a plotly component. + + Args: + name: The name of the component. + package: The package path of the component. + + Returns: + The dynamic import for the plotly component. + """ + return f""" +const {name} = dynamic(() => import('{package}').then(mod => createPlotlyComponent(mod)), {{ssr: false}}) +""" + + +class PlotlyBasic(Plotly): + """Display a basic plotly graph.""" + + tag: str = "BasicPlotlyPlot" + + library = "react-plotly.js@2.6.0" + + lib_dependencies: list[str] = ["plotly.js-basic-dist-min@3.0.0"] + + def add_imports(self) -> ImportDict | list[ImportDict]: + """Add imports for the plotly basic component. + + Returns: + The imports for the plotly basic component. + """ + return CREATE_PLOTLY_COMPONENT + + def _get_dynamic_imports(self) -> str: + """Get the dynamic imports for the plotly basic component. + + Returns: + The dynamic imports for the plotly basic component. + """ + return dynamic_plotly_import(self.tag, "plotly.js-basic-dist-min") + + +class PlotlyCartesian(Plotly): + """Display a plotly cartesian graph.""" + + tag: str = "CartesianPlotlyPlot" + + library = "react-plotly.js@2.6.0" + + lib_dependencies: list[str] = ["plotly.js-cartesian-dist-min@3.0.0"] + + def add_imports(self) -> ImportDict | list[ImportDict]: + """Add imports for the plotly cartesian component. + + Returns: + The imports for the plotly cartesian component. + """ + return CREATE_PLOTLY_COMPONENT + + def _get_dynamic_imports(self) -> str: + """Get the dynamic imports for the plotly cartesian component. + + Returns: + The dynamic imports for the plotly cartesian component. + """ + return dynamic_plotly_import(self.tag, "plotly.js-cartesian-dist-min") + + +class PlotlyGeo(Plotly): + """Display a plotly geo graph.""" + + tag: str = "GeoPlotlyPlot" + + library = "react-plotly.js@2.6.0" + + lib_dependencies: list[str] = ["plotly.js-geo-dist-min@3.0.0"] + + def add_imports(self) -> ImportDict | list[ImportDict]: + """Add imports for the plotly geo component. + + Returns: + The imports for the plotly geo component. + """ + return CREATE_PLOTLY_COMPONENT + + def _get_dynamic_imports(self) -> str: + """Get the dynamic imports for the plotly geo component. + + Returns: + The dynamic imports for the plotly geo component. + """ + return dynamic_plotly_import(self.tag, "plotly.js-geo-dist-min") + + +class PlotlyGl3d(Plotly): + """Display a plotly 3d graph.""" + + tag: str = "Gl3dPlotlyPlot" + + library = "react-plotly.js@2.6.0" + + lib_dependencies: list[str] = ["plotly.js-gl3d-dist-min@3.0.0"] + + def add_imports(self) -> ImportDict | list[ImportDict]: + """Add imports for the plotly 3d component. + + Returns: + The imports for the plotly 3d component. + """ + return CREATE_PLOTLY_COMPONENT + + def _get_dynamic_imports(self) -> str: + """Get the dynamic imports for the plotly 3d component. + + Returns: + The dynamic imports for the plotly 3d component. + """ + return dynamic_plotly_import(self.tag, "plotly.js-gl3d-dist-min") + + +class PlotlyGl2d(Plotly): + """Display a plotly 2d graph.""" + + tag: str = "Gl2dPlotlyPlot" + + library = "react-plotly.js@2.6.0" + + lib_dependencies: list[str] = ["plotly.js-gl2d-dist-min@3.0.0"] + + def add_imports(self) -> ImportDict | list[ImportDict]: + """Add imports for the plotly 2d component. + + Returns: + The imports for the plotly 2d component. + """ + return CREATE_PLOTLY_COMPONENT + + def _get_dynamic_imports(self) -> str: + """Get the dynamic imports for the plotly 2d component. + + Returns: + The dynamic imports for the plotly 2d component. + """ + return dynamic_plotly_import(self.tag, "plotly.js-gl2d-dist-min") + + +class PlotlyMapbox(Plotly): + """Display a plotly mapbox graph.""" + + tag: str = "MapboxPlotlyPlot" + + library = "react-plotly.js@2.6.0" + + lib_dependencies: list[str] = ["plotly.js-mapbox-dist-min@3.0.0"] + + def add_imports(self) -> ImportDict | list[ImportDict]: + """Add imports for the plotly mapbox component. + + Returns: + The imports for the plotly mapbox component. + """ + return CREATE_PLOTLY_COMPONENT + + def _get_dynamic_imports(self) -> str: + """Get the dynamic imports for the plotly mapbox component. + + Returns: + The dynamic imports for the plotly mapbox component. + """ + return dynamic_plotly_import(self.tag, "plotly.js-mapbox-dist-min") + + +class PlotlyFinance(Plotly): + """Display a plotly finance graph.""" + + tag: str = "FinancePlotlyPlot" + + library = "react-plotly.js@2.6.0" + + lib_dependencies: list[str] = ["plotly.js-finance-dist-min@3.0.0"] + + def add_imports(self) -> ImportDict | list[ImportDict]: + """Add imports for the plotly finance component. + + Returns: + The imports for the plotly finance component. + """ + return CREATE_PLOTLY_COMPONENT + + def _get_dynamic_imports(self) -> str: + """Get the dynamic imports for the plotly finance component. + + Returns: + The dynamic imports for the plotly finance component. + """ + return dynamic_plotly_import(self.tag, "plotly.js-finance-dist-min") + + +class PlotlyStrict(Plotly): + """Display a plotly strict graph.""" + + tag: str = "StrictPlotlyPlot" + + library = "react-plotly.js@2.6.0" + + lib_dependencies: list[str] = ["plotly.js-strict-dist-min@3.0.0"] + + def add_imports(self) -> ImportDict | list[ImportDict]: + """Add imports for the plotly strict component. + + Returns: + The imports for the plotly strict component. + """ + return CREATE_PLOTLY_COMPONENT + + def _get_dynamic_imports(self) -> str: + """Get the dynamic imports for the plotly strict component. + + Returns: + The dynamic imports for the plotly strict component. + """ + return dynamic_plotly_import(self.tag, "plotly.js-strict-dist-min") diff --git a/reflex/components/plotly/plotly.pyi b/reflex/components/plotly/plotly.pyi index f60e5a6a4..c4d8bf64a 100644 --- a/reflex/components/plotly/plotly.pyi +++ b/reflex/components/plotly/plotly.pyi @@ -11,6 +11,7 @@ from reflex.components.component import NoSSRComponent from reflex.event import EventType from reflex.style import Style from reflex.utils import console +from reflex.utils.imports import ImportDict from reflex.vars.base import Var try: @@ -141,3 +142,767 @@ class Plotly(NoSSRComponent): The Plotly component. """ ... + +CREATE_PLOTLY_COMPONENT: ImportDict + +def dynamic_plotly_import(name: str, package: str) -> str: ... + +class PlotlyBasic(Plotly): + def add_imports(self) -> ImportDict | list[ImportDict]: ... + @overload + @classmethod + def create( # type: ignore + cls, + *children, + data: Optional[Union[Figure, Var[Figure]]] = None, # type: ignore + layout: Optional[Union[Dict, Var[Dict]]] = None, + template: Optional[Union[Template, Var[Template]]] = None, # type: ignore + config: Optional[Union[Dict, Var[Dict]]] = None, + use_resize_handler: Optional[Union[Var[bool], bool]] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None, + on_after_plot: Optional[EventType[()]] = None, + on_animated: Optional[EventType[()]] = None, + on_animating_frame: Optional[EventType[()]] = None, + on_animation_interrupted: Optional[EventType[()]] = None, + on_autosize: Optional[EventType[()]] = None, + on_before_hover: Optional[EventType[()]] = None, + on_blur: Optional[EventType[()]] = None, + on_button_clicked: Optional[EventType[()]] = None, + on_click: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_context_menu: Optional[EventType[()]] = None, + on_deselect: Optional[EventType[()]] = None, + on_double_click: Optional[EventType[()]] = None, + on_focus: Optional[EventType[()]] = None, + on_hover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_mount: Optional[EventType[()]] = None, + on_mouse_down: Optional[EventType[()]] = None, + on_mouse_enter: Optional[EventType[()]] = None, + on_mouse_leave: Optional[EventType[()]] = None, + on_mouse_move: Optional[EventType[()]] = None, + on_mouse_out: Optional[EventType[()]] = None, + on_mouse_over: Optional[EventType[()]] = None, + on_mouse_up: Optional[EventType[()]] = None, + on_redraw: Optional[EventType[()]] = None, + on_relayout: Optional[EventType[()]] = None, + on_relayouting: Optional[EventType[()]] = None, + on_restyle: Optional[EventType[()]] = None, + on_scroll: Optional[EventType[()]] = None, + on_selected: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_selecting: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_transition_interrupted: Optional[EventType[()]] = None, + on_transitioning: Optional[EventType[()]] = None, + on_unhover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_unmount: Optional[EventType[()]] = None, + **props, + ) -> "PlotlyBasic": + """Create the Plotly component. + + Args: + *children: The children of the component. + data: The figure to display. This can be a plotly figure or a plotly data json. + layout: The layout of the graph. + template: The template for visual appearance of the graph. + config: The config of the graph. + 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 laid out (zoom, pan, etc). + on_relayouting: Fired while the plot is being laid 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 occurring. + 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. + 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 component. + + Returns: + The Plotly component. + """ + ... + +class PlotlyCartesian(Plotly): + def add_imports(self) -> ImportDict | list[ImportDict]: ... + @overload + @classmethod + def create( # type: ignore + cls, + *children, + data: Optional[Union[Figure, Var[Figure]]] = None, # type: ignore + layout: Optional[Union[Dict, Var[Dict]]] = None, + template: Optional[Union[Template, Var[Template]]] = None, # type: ignore + config: Optional[Union[Dict, Var[Dict]]] = None, + use_resize_handler: Optional[Union[Var[bool], bool]] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None, + on_after_plot: Optional[EventType[()]] = None, + on_animated: Optional[EventType[()]] = None, + on_animating_frame: Optional[EventType[()]] = None, + on_animation_interrupted: Optional[EventType[()]] = None, + on_autosize: Optional[EventType[()]] = None, + on_before_hover: Optional[EventType[()]] = None, + on_blur: Optional[EventType[()]] = None, + on_button_clicked: Optional[EventType[()]] = None, + on_click: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_context_menu: Optional[EventType[()]] = None, + on_deselect: Optional[EventType[()]] = None, + on_double_click: Optional[EventType[()]] = None, + on_focus: Optional[EventType[()]] = None, + on_hover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_mount: Optional[EventType[()]] = None, + on_mouse_down: Optional[EventType[()]] = None, + on_mouse_enter: Optional[EventType[()]] = None, + on_mouse_leave: Optional[EventType[()]] = None, + on_mouse_move: Optional[EventType[()]] = None, + on_mouse_out: Optional[EventType[()]] = None, + on_mouse_over: Optional[EventType[()]] = None, + on_mouse_up: Optional[EventType[()]] = None, + on_redraw: Optional[EventType[()]] = None, + on_relayout: Optional[EventType[()]] = None, + on_relayouting: Optional[EventType[()]] = None, + on_restyle: Optional[EventType[()]] = None, + on_scroll: Optional[EventType[()]] = None, + on_selected: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_selecting: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_transition_interrupted: Optional[EventType[()]] = None, + on_transitioning: Optional[EventType[()]] = None, + on_unhover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_unmount: Optional[EventType[()]] = None, + **props, + ) -> "PlotlyCartesian": + """Create the Plotly component. + + Args: + *children: The children of the component. + data: The figure to display. This can be a plotly figure or a plotly data json. + layout: The layout of the graph. + template: The template for visual appearance of the graph. + config: The config of the graph. + 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 laid out (zoom, pan, etc). + on_relayouting: Fired while the plot is being laid 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 occurring. + 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. + 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 component. + + Returns: + The Plotly component. + """ + ... + +class PlotlyGeo(Plotly): + def add_imports(self) -> ImportDict | list[ImportDict]: ... + @overload + @classmethod + def create( # type: ignore + cls, + *children, + data: Optional[Union[Figure, Var[Figure]]] = None, # type: ignore + layout: Optional[Union[Dict, Var[Dict]]] = None, + template: Optional[Union[Template, Var[Template]]] = None, # type: ignore + config: Optional[Union[Dict, Var[Dict]]] = None, + use_resize_handler: Optional[Union[Var[bool], bool]] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None, + on_after_plot: Optional[EventType[()]] = None, + on_animated: Optional[EventType[()]] = None, + on_animating_frame: Optional[EventType[()]] = None, + on_animation_interrupted: Optional[EventType[()]] = None, + on_autosize: Optional[EventType[()]] = None, + on_before_hover: Optional[EventType[()]] = None, + on_blur: Optional[EventType[()]] = None, + on_button_clicked: Optional[EventType[()]] = None, + on_click: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_context_menu: Optional[EventType[()]] = None, + on_deselect: Optional[EventType[()]] = None, + on_double_click: Optional[EventType[()]] = None, + on_focus: Optional[EventType[()]] = None, + on_hover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_mount: Optional[EventType[()]] = None, + on_mouse_down: Optional[EventType[()]] = None, + on_mouse_enter: Optional[EventType[()]] = None, + on_mouse_leave: Optional[EventType[()]] = None, + on_mouse_move: Optional[EventType[()]] = None, + on_mouse_out: Optional[EventType[()]] = None, + on_mouse_over: Optional[EventType[()]] = None, + on_mouse_up: Optional[EventType[()]] = None, + on_redraw: Optional[EventType[()]] = None, + on_relayout: Optional[EventType[()]] = None, + on_relayouting: Optional[EventType[()]] = None, + on_restyle: Optional[EventType[()]] = None, + on_scroll: Optional[EventType[()]] = None, + on_selected: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_selecting: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_transition_interrupted: Optional[EventType[()]] = None, + on_transitioning: Optional[EventType[()]] = None, + on_unhover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_unmount: Optional[EventType[()]] = None, + **props, + ) -> "PlotlyGeo": + """Create the Plotly component. + + Args: + *children: The children of the component. + data: The figure to display. This can be a plotly figure or a plotly data json. + layout: The layout of the graph. + template: The template for visual appearance of the graph. + config: The config of the graph. + 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 laid out (zoom, pan, etc). + on_relayouting: Fired while the plot is being laid 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 occurring. + 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. + 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 component. + + Returns: + The Plotly component. + """ + ... + +class PlotlyGl3d(Plotly): + def add_imports(self) -> ImportDict | list[ImportDict]: ... + @overload + @classmethod + def create( # type: ignore + cls, + *children, + data: Optional[Union[Figure, Var[Figure]]] = None, # type: ignore + layout: Optional[Union[Dict, Var[Dict]]] = None, + template: Optional[Union[Template, Var[Template]]] = None, # type: ignore + config: Optional[Union[Dict, Var[Dict]]] = None, + use_resize_handler: Optional[Union[Var[bool], bool]] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None, + on_after_plot: Optional[EventType[()]] = None, + on_animated: Optional[EventType[()]] = None, + on_animating_frame: Optional[EventType[()]] = None, + on_animation_interrupted: Optional[EventType[()]] = None, + on_autosize: Optional[EventType[()]] = None, + on_before_hover: Optional[EventType[()]] = None, + on_blur: Optional[EventType[()]] = None, + on_button_clicked: Optional[EventType[()]] = None, + on_click: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_context_menu: Optional[EventType[()]] = None, + on_deselect: Optional[EventType[()]] = None, + on_double_click: Optional[EventType[()]] = None, + on_focus: Optional[EventType[()]] = None, + on_hover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_mount: Optional[EventType[()]] = None, + on_mouse_down: Optional[EventType[()]] = None, + on_mouse_enter: Optional[EventType[()]] = None, + on_mouse_leave: Optional[EventType[()]] = None, + on_mouse_move: Optional[EventType[()]] = None, + on_mouse_out: Optional[EventType[()]] = None, + on_mouse_over: Optional[EventType[()]] = None, + on_mouse_up: Optional[EventType[()]] = None, + on_redraw: Optional[EventType[()]] = None, + on_relayout: Optional[EventType[()]] = None, + on_relayouting: Optional[EventType[()]] = None, + on_restyle: Optional[EventType[()]] = None, + on_scroll: Optional[EventType[()]] = None, + on_selected: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_selecting: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_transition_interrupted: Optional[EventType[()]] = None, + on_transitioning: Optional[EventType[()]] = None, + on_unhover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_unmount: Optional[EventType[()]] = None, + **props, + ) -> "PlotlyGl3d": + """Create the Plotly component. + + Args: + *children: The children of the component. + data: The figure to display. This can be a plotly figure or a plotly data json. + layout: The layout of the graph. + template: The template for visual appearance of the graph. + config: The config of the graph. + 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 laid out (zoom, pan, etc). + on_relayouting: Fired while the plot is being laid 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 occurring. + 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. + 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 component. + + Returns: + The Plotly component. + """ + ... + +class PlotlyGl2d(Plotly): + def add_imports(self) -> ImportDict | list[ImportDict]: ... + @overload + @classmethod + def create( # type: ignore + cls, + *children, + data: Optional[Union[Figure, Var[Figure]]] = None, # type: ignore + layout: Optional[Union[Dict, Var[Dict]]] = None, + template: Optional[Union[Template, Var[Template]]] = None, # type: ignore + config: Optional[Union[Dict, Var[Dict]]] = None, + use_resize_handler: Optional[Union[Var[bool], bool]] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None, + on_after_plot: Optional[EventType[()]] = None, + on_animated: Optional[EventType[()]] = None, + on_animating_frame: Optional[EventType[()]] = None, + on_animation_interrupted: Optional[EventType[()]] = None, + on_autosize: Optional[EventType[()]] = None, + on_before_hover: Optional[EventType[()]] = None, + on_blur: Optional[EventType[()]] = None, + on_button_clicked: Optional[EventType[()]] = None, + on_click: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_context_menu: Optional[EventType[()]] = None, + on_deselect: Optional[EventType[()]] = None, + on_double_click: Optional[EventType[()]] = None, + on_focus: Optional[EventType[()]] = None, + on_hover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_mount: Optional[EventType[()]] = None, + on_mouse_down: Optional[EventType[()]] = None, + on_mouse_enter: Optional[EventType[()]] = None, + on_mouse_leave: Optional[EventType[()]] = None, + on_mouse_move: Optional[EventType[()]] = None, + on_mouse_out: Optional[EventType[()]] = None, + on_mouse_over: Optional[EventType[()]] = None, + on_mouse_up: Optional[EventType[()]] = None, + on_redraw: Optional[EventType[()]] = None, + on_relayout: Optional[EventType[()]] = None, + on_relayouting: Optional[EventType[()]] = None, + on_restyle: Optional[EventType[()]] = None, + on_scroll: Optional[EventType[()]] = None, + on_selected: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_selecting: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_transition_interrupted: Optional[EventType[()]] = None, + on_transitioning: Optional[EventType[()]] = None, + on_unhover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_unmount: Optional[EventType[()]] = None, + **props, + ) -> "PlotlyGl2d": + """Create the Plotly component. + + Args: + *children: The children of the component. + data: The figure to display. This can be a plotly figure or a plotly data json. + layout: The layout of the graph. + template: The template for visual appearance of the graph. + config: The config of the graph. + 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 laid out (zoom, pan, etc). + on_relayouting: Fired while the plot is being laid 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 occurring. + 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. + 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 component. + + Returns: + The Plotly component. + """ + ... + +class PlotlyMapbox(Plotly): + def add_imports(self) -> ImportDict | list[ImportDict]: ... + @overload + @classmethod + def create( # type: ignore + cls, + *children, + data: Optional[Union[Figure, Var[Figure]]] = None, # type: ignore + layout: Optional[Union[Dict, Var[Dict]]] = None, + template: Optional[Union[Template, Var[Template]]] = None, # type: ignore + config: Optional[Union[Dict, Var[Dict]]] = None, + use_resize_handler: Optional[Union[Var[bool], bool]] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None, + on_after_plot: Optional[EventType[()]] = None, + on_animated: Optional[EventType[()]] = None, + on_animating_frame: Optional[EventType[()]] = None, + on_animation_interrupted: Optional[EventType[()]] = None, + on_autosize: Optional[EventType[()]] = None, + on_before_hover: Optional[EventType[()]] = None, + on_blur: Optional[EventType[()]] = None, + on_button_clicked: Optional[EventType[()]] = None, + on_click: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_context_menu: Optional[EventType[()]] = None, + on_deselect: Optional[EventType[()]] = None, + on_double_click: Optional[EventType[()]] = None, + on_focus: Optional[EventType[()]] = None, + on_hover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_mount: Optional[EventType[()]] = None, + on_mouse_down: Optional[EventType[()]] = None, + on_mouse_enter: Optional[EventType[()]] = None, + on_mouse_leave: Optional[EventType[()]] = None, + on_mouse_move: Optional[EventType[()]] = None, + on_mouse_out: Optional[EventType[()]] = None, + on_mouse_over: Optional[EventType[()]] = None, + on_mouse_up: Optional[EventType[()]] = None, + on_redraw: Optional[EventType[()]] = None, + on_relayout: Optional[EventType[()]] = None, + on_relayouting: Optional[EventType[()]] = None, + on_restyle: Optional[EventType[()]] = None, + on_scroll: Optional[EventType[()]] = None, + on_selected: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_selecting: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_transition_interrupted: Optional[EventType[()]] = None, + on_transitioning: Optional[EventType[()]] = None, + on_unhover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_unmount: Optional[EventType[()]] = None, + **props, + ) -> "PlotlyMapbox": + """Create the Plotly component. + + Args: + *children: The children of the component. + data: The figure to display. This can be a plotly figure or a plotly data json. + layout: The layout of the graph. + template: The template for visual appearance of the graph. + config: The config of the graph. + 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 laid out (zoom, pan, etc). + on_relayouting: Fired while the plot is being laid 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 occurring. + 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. + 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 component. + + Returns: + The Plotly component. + """ + ... + +class PlotlyFinance(Plotly): + def add_imports(self) -> ImportDict | list[ImportDict]: ... + @overload + @classmethod + def create( # type: ignore + cls, + *children, + data: Optional[Union[Figure, Var[Figure]]] = None, # type: ignore + layout: Optional[Union[Dict, Var[Dict]]] = None, + template: Optional[Union[Template, Var[Template]]] = None, # type: ignore + config: Optional[Union[Dict, Var[Dict]]] = None, + use_resize_handler: Optional[Union[Var[bool], bool]] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None, + on_after_plot: Optional[EventType[()]] = None, + on_animated: Optional[EventType[()]] = None, + on_animating_frame: Optional[EventType[()]] = None, + on_animation_interrupted: Optional[EventType[()]] = None, + on_autosize: Optional[EventType[()]] = None, + on_before_hover: Optional[EventType[()]] = None, + on_blur: Optional[EventType[()]] = None, + on_button_clicked: Optional[EventType[()]] = None, + on_click: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_context_menu: Optional[EventType[()]] = None, + on_deselect: Optional[EventType[()]] = None, + on_double_click: Optional[EventType[()]] = None, + on_focus: Optional[EventType[()]] = None, + on_hover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_mount: Optional[EventType[()]] = None, + on_mouse_down: Optional[EventType[()]] = None, + on_mouse_enter: Optional[EventType[()]] = None, + on_mouse_leave: Optional[EventType[()]] = None, + on_mouse_move: Optional[EventType[()]] = None, + on_mouse_out: Optional[EventType[()]] = None, + on_mouse_over: Optional[EventType[()]] = None, + on_mouse_up: Optional[EventType[()]] = None, + on_redraw: Optional[EventType[()]] = None, + on_relayout: Optional[EventType[()]] = None, + on_relayouting: Optional[EventType[()]] = None, + on_restyle: Optional[EventType[()]] = None, + on_scroll: Optional[EventType[()]] = None, + on_selected: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_selecting: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_transition_interrupted: Optional[EventType[()]] = None, + on_transitioning: Optional[EventType[()]] = None, + on_unhover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_unmount: Optional[EventType[()]] = None, + **props, + ) -> "PlotlyFinance": + """Create the Plotly component. + + Args: + *children: The children of the component. + data: The figure to display. This can be a plotly figure or a plotly data json. + layout: The layout of the graph. + template: The template for visual appearance of the graph. + config: The config of the graph. + 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 laid out (zoom, pan, etc). + on_relayouting: Fired while the plot is being laid 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 occurring. + 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. + 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 component. + + Returns: + The Plotly component. + """ + ... + +class PlotlyStrict(Plotly): + def add_imports(self) -> ImportDict | list[ImportDict]: ... + @overload + @classmethod + def create( # type: ignore + cls, + *children, + data: Optional[Union[Figure, Var[Figure]]] = None, # type: ignore + layout: Optional[Union[Dict, Var[Dict]]] = None, + template: Optional[Union[Template, Var[Template]]] = None, # type: ignore + config: Optional[Union[Dict, Var[Dict]]] = None, + use_resize_handler: Optional[Union[Var[bool], bool]] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None, + on_after_plot: Optional[EventType[()]] = None, + on_animated: Optional[EventType[()]] = None, + on_animating_frame: Optional[EventType[()]] = None, + on_animation_interrupted: Optional[EventType[()]] = None, + on_autosize: Optional[EventType[()]] = None, + on_before_hover: Optional[EventType[()]] = None, + on_blur: Optional[EventType[()]] = None, + on_button_clicked: Optional[EventType[()]] = None, + on_click: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_context_menu: Optional[EventType[()]] = None, + on_deselect: Optional[EventType[()]] = None, + on_double_click: Optional[EventType[()]] = None, + on_focus: Optional[EventType[()]] = None, + on_hover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_mount: Optional[EventType[()]] = None, + on_mouse_down: Optional[EventType[()]] = None, + on_mouse_enter: Optional[EventType[()]] = None, + on_mouse_leave: Optional[EventType[()]] = None, + on_mouse_move: Optional[EventType[()]] = None, + on_mouse_out: Optional[EventType[()]] = None, + on_mouse_over: Optional[EventType[()]] = None, + on_mouse_up: Optional[EventType[()]] = None, + on_redraw: Optional[EventType[()]] = None, + on_relayout: Optional[EventType[()]] = None, + on_relayouting: Optional[EventType[()]] = None, + on_restyle: Optional[EventType[()]] = None, + on_scroll: Optional[EventType[()]] = None, + on_selected: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_selecting: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_transition_interrupted: Optional[EventType[()]] = None, + on_transitioning: Optional[EventType[()]] = None, + on_unhover: Optional[Union[EventType[()], EventType[List[Point]]]] = None, + on_unmount: Optional[EventType[()]] = None, + **props, + ) -> "PlotlyStrict": + """Create the Plotly component. + + Args: + *children: The children of the component. + data: The figure to display. This can be a plotly figure or a plotly data json. + layout: The layout of the graph. + template: The template for visual appearance of the graph. + config: The config of the graph. + 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 laid out (zoom, pan, etc). + on_relayouting: Fired while the plot is being laid 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 occurring. + 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. + 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 component. + + Returns: + The Plotly component. + """ + ... diff --git a/reflex/config.py b/reflex/config.py index aa8c61d10..511818d94 100644 --- a/reflex/config.py +++ b/reflex/config.py @@ -679,7 +679,7 @@ class Config(Base): # Number of gunicorn workers from user gunicorn_workers: Optional[int] = None - # Number of requests before a worker is restarted + # Number of requests before a worker is restarted; set to 0 to disable gunicorn_max_requests: int = 100 # Variance limit for max requests; gunicorn only diff --git a/reflex/event.py b/reflex/event.py index f247047cf..c2eb8db3a 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -26,6 +26,7 @@ from typing import ( from typing_extensions import ( Protocol, + Self, TypeAliasType, TypedDict, TypeVar, @@ -110,7 +111,7 @@ class EventActionsMixin: event_actions: Dict[str, Union[bool, int]] = dataclasses.field(default_factory=dict) @property - def stop_propagation(self): + def stop_propagation(self) -> Self: """Stop the event from bubbling up the DOM tree. Returns: @@ -122,7 +123,7 @@ class EventActionsMixin: ) @property - def prevent_default(self): + def prevent_default(self) -> Self: """Prevent the default behavior of the event. Returns: @@ -133,7 +134,7 @@ class EventActionsMixin: event_actions={"preventDefault": True, **self.event_actions}, ) - def throttle(self, limit_ms: int): + def throttle(self, limit_ms: int) -> Self: """Throttle the event handler. Args: @@ -147,7 +148,7 @@ class EventActionsMixin: event_actions={"throttle": limit_ms, **self.event_actions}, ) - def debounce(self, delay_ms: int): + def debounce(self, delay_ms: int) -> Self: """Debounce the event handler. Args: @@ -162,7 +163,7 @@ class EventActionsMixin: ) @property - def temporal(self): + def temporal(self) -> Self: """Do not queue the event if the backend is down. Returns: @@ -1773,7 +1774,7 @@ V4 = TypeVar("V4") V5 = TypeVar("V5") -class EventCallback(Generic[Unpack[P]]): +class EventCallback(Generic[Unpack[P]], EventActionsMixin): """A descriptor that wraps a function to be used as an event.""" def __init__(self, func: Callable[[Any, Unpack[P]], Any]): @@ -1784,24 +1785,6 @@ class EventCallback(Generic[Unpack[P]]): """ self.func = func - @property - def prevent_default(self): - """Prevent default behavior. - - Returns: - The event callback with prevent default behavior. - """ - return self - - @property - def stop_propagation(self): - """Stop event propagation. - - Returns: - The event callback with stop propagation behavior. - """ - return self - @overload def __call__( self: EventCallback[Unpack[Q]], diff --git a/reflex/reflex.py b/reflex/reflex.py index 70aa16a05..e4be0c89a 100644 --- a/reflex/reflex.py +++ b/reflex/reflex.py @@ -145,10 +145,7 @@ def _run( exec.output_system_info() # If no --frontend-only and no --backend-only, then turn on frontend and backend both - if not frontend and not backend: - frontend = True - backend = True - + frontend, backend = prerequisites.check_running_mode(frontend, backend) if not frontend and backend: _skip_compile() @@ -306,10 +303,18 @@ def export( True, "--no-zip", help="Disable zip for backend and frontend exports." ), frontend: bool = typer.Option( - True, "--backend-only", help="Export only backend.", show_default=False + False, + "--frontend-only", + help="Export only frontend.", + show_default=False, + envvar=environment.REFLEX_FRONTEND_ONLY.name, ), backend: bool = typer.Option( - True, "--frontend-only", help="Export only frontend.", show_default=False + False, + "--backend-only", + help="Export only backend.", + show_default=False, + envvar=environment.REFLEX_BACKEND_ONLY.name, ), zip_dest_dir: str = typer.Option( str(Path.cwd()), @@ -332,7 +337,9 @@ def export( from reflex.utils import export as export_utils from reflex.utils import prerequisites - if prerequisites.needs_reinit(frontend=True): + frontend, backend = prerequisites.check_running_mode(frontend, backend) + + if prerequisites.needs_reinit(frontend=frontend or not backend): _init(name=config.app_name, loglevel=loglevel) if frontend and not config.show_built_with_reflex: diff --git a/reflex/utils/console.py b/reflex/utils/console.py index d5b7a0d6e..5c47eee6f 100644 --- a/reflex/utils/console.py +++ b/reflex/utils/console.py @@ -2,8 +2,10 @@ from __future__ import annotations +import contextlib import inspect import shutil +import time from pathlib import Path from types import FrameType @@ -317,3 +319,20 @@ def status(*args, **kwargs): A new status. """ return _console.status(*args, **kwargs) + + +@contextlib.contextmanager +def timing(msg: str): + """Create a context manager to time a block of code. + + Args: + msg: The message to display. + + Yields: + None. + """ + start = time.time() + try: + yield + finally: + debug(f"[white]\\[timing] {msg}: {time.time() - start:.2f}s[/white]") diff --git a/reflex/utils/exec.py b/reflex/utils/exec.py index 67df7ea91..de326dacc 100644 --- a/reflex/utils/exec.py +++ b/reflex/utils/exec.py @@ -368,34 +368,49 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel): app_module = get_app_module() - run_backend_prod = f"gunicorn --worker-class {config.gunicorn_worker_class} --max-requests {config.gunicorn_max_requests} --max-requests-jitter {config.gunicorn_max_requests_jitter} --preload --timeout {config.timeout} --log-level critical".split() - run_backend_prod_windows = f"uvicorn --limit-max-requests {config.gunicorn_max_requests} --timeout-keep-alive {config.timeout}".split() command = ( [ - *run_backend_prod_windows, - "--host", - host, - "--port", - str(port), + "uvicorn", + *( + [ + "--limit-max-requests", + str(config.gunicorn_max_requests), + ] + if config.gunicorn_max_requests > 0 + else [] + ), + *("--timeout-keep-alive", str(config.timeout)), + *("--host", host), + *("--port", str(port)), + *("--workers", str(_get_backend_workers())), app_module, ] if constants.IS_WINDOWS else [ - *run_backend_prod, - "--bind", - f"{host}:{port}", - "--threads", - str(_get_backend_workers()), + "gunicorn", + *("--worker-class", config.gunicorn_worker_class), + *( + [ + "--max-requests", + str(config.gunicorn_max_requests), + "--max-requests-jitter", + str(config.gunicorn_max_requests_jitter), + ] + if config.gunicorn_max_requests > 0 + else [] + ), + "--preload", + *("--timeout", str(config.timeout)), + *("--bind", f"{host}:{port}"), + *("--threads", str(_get_backend_workers())), f"{app_module}()", ] ) command += [ - "--log-level", - loglevel.value, - "--workers", - str(_get_backend_workers()), + *("--log-level", loglevel.value), ] + processes.new_process( command, run=True, diff --git a/reflex/utils/imports.py b/reflex/utils/imports.py index 46e8e7362..66ae4b023 100644 --- a/reflex/utils/imports.py +++ b/reflex/utils/imports.py @@ -109,6 +109,9 @@ class ImportVar: # whether this import should be rendered or not render: Optional[bool] = True + # The path of the package to import from. + package_path: str = "/" + # whether this import package should be added to transpilePackages in next.config.js # https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages transpile: Optional[bool] = False diff --git a/reflex/utils/path_ops.py b/reflex/utils/path_ops.py index 07a541201..dae938316 100644 --- a/reflex/utils/path_ops.py +++ b/reflex/utils/path_ops.py @@ -260,3 +260,49 @@ def find_replace(directory: str | Path, find: str, replace: str): text = filepath.read_text(encoding="utf-8") text = re.sub(find, replace, text) filepath.write_text(text, encoding="utf-8") + + +def samefile(file1: Path, file2: Path) -> bool: + """Check if two files are the same. + + Args: + file1: The first file. + file2: The second file. + + Returns: + Whether the files are the same. If either file does not exist, returns False. + """ + if file1.exists() and file2.exists(): + return file1.samefile(file2) + + return False + + +def update_directory_tree(src: Path, dest: Path): + """Recursively copies a directory tree from src to dest. + Only copies files if the destination file is missing or modified earlier than the source file. + + Args: + src: Source directory + dest: Destination directory + + Raises: + ValueError: If the source is not a directory + """ + if not src.is_dir(): + raise ValueError(f"Source {src} is not a directory") + + # Ensure the destination directory exists + dest.mkdir(parents=True, exist_ok=True) + + for item in src.iterdir(): + dest_item = dest / item.name + + if item.is_dir(): + # Recursively copy subdirectories + update_directory_tree(item, dest_item) + elif item.is_file() and ( + not dest_item.exists() or item.stat().st_mtime > dest_item.stat().st_mtime + ): + # Copy file if it doesn't exist in the destination or is older than the source + shutil.copy2(item, dest_item) diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index 6c6d34923..8047e1256 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -1225,6 +1225,21 @@ def install_frontend_packages(packages: set[str], config: Config): ) +def check_running_mode(frontend: bool, backend: bool) -> tuple[bool, bool]: + """Check if the app is running in frontend or backend mode. + + Args: + frontend: Whether to run the frontend of the app. + backend: Whether to run the backend of the app. + + Returns: + The running modes. + """ + if not frontend and not backend: + return True, True + return frontend, backend + + def needs_reinit(frontend: bool = True) -> bool: """Check if an app needs to be reinitialized. @@ -1293,10 +1308,13 @@ def validate_bun(): """ bun_path = path_ops.get_bun_path() - if bun_path and not bun_path.samefile(constants.Bun.DEFAULT_PATH): + if bun_path is None: + return + + if not path_ops.samefile(bun_path, constants.Bun.DEFAULT_PATH): console.info(f"Using custom Bun path: {bun_path}") bun_version = get_bun_version() - if not bun_version: + if bun_version is None: console.error( "Failed to obtain bun version. Make sure the specified bun path in your config is correct." ) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index c9dd81986..a24db4010 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -75,9 +75,9 @@ from reflex.utils.types import ( if TYPE_CHECKING: from reflex.state import BaseState - from .number import BooleanVar, NumberVar - from .object import ObjectVar - from .sequence import ArrayVar, StringVar + from .number import BooleanVar, LiteralBooleanVar, LiteralNumberVar, NumberVar + from .object import LiteralObjectVar, ObjectVar + from .sequence import ArrayVar, LiteralArrayVar, LiteralStringVar, StringVar VAR_TYPE = TypeVar("VAR_TYPE", covariant=True) @@ -573,13 +573,21 @@ class Var(Generic[VAR_TYPE]): return value_with_replaced + @overload + @classmethod + def create( # pyright: ignore[reportOverlappingOverload] + cls, + value: NoReturn, + _var_data: VarData | None = None, + ) -> Var[Any]: ... + @overload @classmethod def create( # pyright: ignore[reportOverlappingOverload] cls, value: bool, _var_data: VarData | None = None, - ) -> BooleanVar: ... + ) -> LiteralBooleanVar: ... @overload @classmethod @@ -587,7 +595,7 @@ class Var(Generic[VAR_TYPE]): cls, value: int, _var_data: VarData | None = None, - ) -> NumberVar[int]: ... + ) -> LiteralNumberVar[int]: ... @overload @classmethod @@ -595,7 +603,15 @@ class Var(Generic[VAR_TYPE]): cls, value: float, _var_data: VarData | None = None, - ) -> NumberVar[float]: ... + ) -> LiteralNumberVar[float]: ... + + @overload + @classmethod + def create( # pyright: ignore [reportOverlappingOverload] + cls, + value: str, + _var_data: VarData | None = None, + ) -> LiteralStringVar: ... @overload @classmethod @@ -611,7 +627,7 @@ class Var(Generic[VAR_TYPE]): cls, value: None, _var_data: VarData | None = None, - ) -> NoneVar: ... + ) -> LiteralNoneVar: ... @overload @classmethod @@ -619,7 +635,7 @@ class Var(Generic[VAR_TYPE]): cls, value: MAPPING_TYPE, _var_data: VarData | None = None, - ) -> ObjectVar[MAPPING_TYPE]: ... + ) -> LiteralObjectVar[MAPPING_TYPE]: ... @overload @classmethod @@ -627,7 +643,7 @@ class Var(Generic[VAR_TYPE]): cls, value: SEQUENCE_TYPE, _var_data: VarData | None = None, - ) -> ArrayVar[SEQUENCE_TYPE]: ... + ) -> LiteralArrayVar[SEQUENCE_TYPE]: ... @overload @classmethod @@ -2238,6 +2254,27 @@ class ComputedVar(Var[RETURN_TYPE]): owner: Type, ) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ... + @overload + def __get__( + self: ComputedVar[BASE_TYPE], + instance: None, + owner: Type, + ) -> ObjectVar[BASE_TYPE]: ... + + @overload + def __get__( + self: ComputedVar[SQLA_TYPE], + instance: None, + owner: Type, + ) -> ObjectVar[SQLA_TYPE]: ... + + if TYPE_CHECKING: + + @overload + def __get__( + self: ComputedVar[DATACLASS_TYPE], instance: None, owner: Any + ) -> ObjectVar[DATACLASS_TYPE]: ... + @overload def __get__(self, instance: None, owner: Type) -> ComputedVar[RETURN_TYPE]: ... @@ -2484,6 +2521,27 @@ class AsyncComputedVar(ComputedVar[RETURN_TYPE]): owner: Type, ) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ... + @overload + def __get__( + self: AsyncComputedVar[BASE_TYPE], + instance: None, + owner: Type, + ) -> ObjectVar[BASE_TYPE]: ... + + @overload + def __get__( + self: AsyncComputedVar[SQLA_TYPE], + instance: None, + owner: Type, + ) -> ObjectVar[SQLA_TYPE]: ... + + if TYPE_CHECKING: + + @overload + def __get__( + self: AsyncComputedVar[DATACLASS_TYPE], instance: None, owner: Any + ) -> ObjectVar[DATACLASS_TYPE]: ... + @overload def __get__(self, instance: None, owner: Type) -> AsyncComputedVar[RETURN_TYPE]: ... diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 35a55490a..87f1760a6 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -974,7 +974,7 @@ def boolean_not_operation(value: BooleanVar): frozen=True, slots=True, ) -class LiteralNumberVar(LiteralVar, NumberVar): +class LiteralNumberVar(LiteralVar, NumberVar[NUMBER_T]): """Base class for immutable literal number vars.""" _var_value: float | int = dataclasses.field(default=0) diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index fb797b4ec..0e7b082f9 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -372,6 +372,33 @@ class StringVar(Var[STRING_TYPE], python_types=str): return string_ge_operation(self, other) + @overload + def replace( # pyright: ignore [reportOverlappingOverload] + self, search_value: StringVar | str, new_value: StringVar | str + ) -> StringVar: ... + + @overload + def replace( + self, search_value: Any, new_value: Any + ) -> CustomVarOperationReturn[StringVar]: ... + + def replace(self, search_value: Any, new_value: Any) -> StringVar: # pyright: ignore [reportInconsistentOverload] + """Replace a string with a value. + + Args: + search_value: The string to search. + new_value: The value to be replaced with. + + Returns: + The string replace operation. + """ + if not isinstance(search_value, (StringVar, str)): + raise_unsupported_operand_types("replace", (type(self), type(search_value))) + if not isinstance(new_value, (StringVar, str)): + raise_unsupported_operand_types("replace", (type(self), type(new_value))) + + return string_replace_operation(self, search_value, new_value) + @var_operation def string_lt_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): @@ -570,7 +597,7 @@ def array_join_operation(array: ArrayVar, sep: StringVar[Any] | str = ""): @var_operation def string_replace_operation( - string: StringVar, search_value: StringVar | str, new_value: StringVar | str + string: StringVar[Any], search_value: StringVar | str, new_value: StringVar | str ): """Replace a string with a value. @@ -583,7 +610,7 @@ def string_replace_operation( The string replace operation. """ return var_operation_return( - js_expression=f"{string}.replace({search_value}, {new_value})", + js_expression=f"{string}.replaceAll({search_value}, {new_value})", var_type=str, ) diff --git a/tests/units/components/datadisplay/test_shiki_code.py b/tests/units/components/datadisplay/test_shiki_code.py index cc05c35b0..e1c7984f1 100644 --- a/tests/units/components/datadisplay/test_shiki_code.py +++ b/tests/units/components/datadisplay/test_shiki_code.py @@ -11,6 +11,7 @@ from reflex.components.lucide.icon import Icon from reflex.components.radix.themes.layout.box import Box from reflex.style import Style from reflex.vars import Var +from reflex.vars.base import LiteralVar @pytest.mark.parametrize( @@ -99,7 +100,9 @@ def test_create_shiki_code_block( applied_styles = component.style for key, value in expected_styles.items(): - assert Var.create(applied_styles[key])._var_value == value + var = Var.create(applied_styles[key]) + assert isinstance(var, LiteralVar) + assert var._var_value == value @pytest.mark.parametrize( diff --git a/tests/units/utils/test_utils.py b/tests/units/utils/test_utils.py index 7cd53f14a..74dcf79b0 100644 --- a/tests/units/utils/test_utils.py +++ b/tests/units/utils/test_utils.py @@ -115,7 +115,20 @@ def test_typehint_issubclass(subclass, superclass, expected): assert types.typehint_issubclass(subclass, superclass) == expected -def test_validate_invalid_bun_path(mocker): +def test_validate_none_bun_path(mocker): + """Test that an error is thrown when a bun path is not specified. + + Args: + mocker: Pytest mocker object. + """ + mocker.patch("reflex.utils.path_ops.get_bun_path", return_value=None) + # with pytest.raises(typer.Exit): + prerequisites.validate_bun() + + +def test_validate_invalid_bun_path( + mocker, +): """Test that an error is thrown when a custom specified bun path is not valid or does not exist. @@ -123,13 +136,12 @@ def test_validate_invalid_bun_path(mocker): mocker: Pytest mocker object. """ mock_path = mocker.Mock() - mock_path.samefile.return_value = False mocker.patch("reflex.utils.path_ops.get_bun_path", return_value=mock_path) + mocker.patch("reflex.utils.path_ops.samefile", return_value=False) mocker.patch("reflex.utils.prerequisites.get_bun_version", return_value=None) with pytest.raises(typer.Exit): prerequisites.validate_bun() - mock_path.samefile.assert_called_once() def test_validate_bun_path_incompatible_version(mocker): @@ -141,6 +153,7 @@ def test_validate_bun_path_incompatible_version(mocker): mock_path = mocker.Mock() mock_path.samefile.return_value = False mocker.patch("reflex.utils.path_ops.get_bun_path", return_value=mock_path) + mocker.patch("reflex.utils.path_ops.samefile", return_value=False) mocker.patch( "reflex.utils.prerequisites.get_bun_version", return_value=version.parse("0.6.5"), diff --git a/tests/units/vars/test_object.py b/tests/units/vars/test_object.py index 90e34be96..89ace55bb 100644 --- a/tests/units/vars/test_object.py +++ b/tests/units/vars/test_object.py @@ -74,11 +74,11 @@ class ObjectState(rx.State): @pytest.mark.parametrize("type_", [Base, Bare, SqlaModel, Dataclass]) -def test_var_create(type_: GenericType) -> None: +def test_var_create(type_: type[Base | Bare | SqlaModel | Dataclass]) -> None: my_object = type_() var = Var.create(my_object) assert var._var_type is type_ - + assert isinstance(var, ObjectVar) quantity = var.quantity assert quantity._var_type is int @@ -94,12 +94,12 @@ def test_literal_create(type_: GenericType) -> None: @pytest.mark.parametrize("type_", [Base, Bare, SqlaModel, Dataclass]) -def test_guess(type_: GenericType) -> None: +def test_guess(type_: type[Base | Bare | SqlaModel | Dataclass]) -> None: my_object = type_() var = Var.create(my_object) var = var.guess_type() assert var._var_type is type_ - + assert isinstance(var, ObjectVar) quantity = var.quantity assert quantity._var_type is int