From 75b63cbc258dd17595c9427fa9b7664afbf82434 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Tue, 5 Mar 2024 12:07:20 -0800 Subject: [PATCH] [REF-2087] Better rx.progress styling integration with radix themes (#2762) * [REF-2087] Better rx.progress styling integration with radix themes Support the `radius` prop on `ProgressRoot`, via data-radius and CSS tokens Support the `color_scheme` on `ProgressIndicator`, via data-accent-color and CSS token Move high-level `Progress` to a real `Component` subclass to get better pyi hinting Allow overriding the background color of the `ProgressIndicator` via low-level api Remove `value` and `max` props from `ProgressRoot` (these only apply to `ProgressIndicator`) * Progress: do not pass `value` or `max` to ProgressRoot * progress: use background_color instead of background-color --- .../components/radix/primitives/progress.py | 85 +++-- .../components/radix/primitives/progress.pyi | 317 +++++++++++++++++- 2 files changed, 348 insertions(+), 54 deletions(-) diff --git a/reflex/components/radix/primitives/progress.py b/reflex/components/radix/primitives/progress.py index ac30cf435..08fa14b05 100644 --- a/reflex/components/radix/primitives/progress.py +++ b/reflex/components/radix/primitives/progress.py @@ -2,13 +2,13 @@ from __future__ import annotations -from typing import Optional, Union +from typing import Optional from reflex.components.component import Component, ComponentNamespace from reflex.components.core.colors import color from reflex.components.radix.primitives.accordion import DEFAULT_ANIMATION_DURATION from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName -from reflex.components.radix.themes.base import LiteralAccentColor +from reflex.components.radix.themes.base import LiteralAccentColor, LiteralRadius from reflex.style import Style from reflex.vars import Var @@ -25,19 +25,19 @@ class ProgressRoot(ProgressComponent): tag = "Root" alias = "RadixProgressRoot" - # The current progress value. - value: Var[Optional[int]] - - # The maximum progress value. - max: Var[int] + # Override theme radius for progress bar: "none" | "small" | "medium" | "large" | "full" + radius: Var[LiteralRadius] def _apply_theme(self, theme: Component): + if self.radius is not None: + self.custom_attrs["data-radius"] = self.radius + self.style = Style( { "position": "relative", "overflow": "hidden", "background": "var(--gray-a3)", - "border_radius": "99999px", + "border_radius": "max(var(--radius-2), var(--radius-full))", "width": "100%", "height": "20px", "boxShadow": "inset 0 0 0 1px var(--gray-a5)", @@ -45,6 +45,9 @@ class ProgressRoot(ProgressComponent): } ) + def _exclude_props(self) -> list[str]: + return ["radius"] + class ProgressIndicator(ProgressComponent): """The Progress bar indicator.""" @@ -63,22 +66,12 @@ class ProgressIndicator(ProgressComponent): color_scheme: Var[LiteralAccentColor] def _apply_theme(self, theme: Component): - global_color_scheme = getattr(theme, "accent_color", None) - - if global_color_scheme is None and self.color_scheme is None: - raise ValueError( - "`color_scheme` cannot be None. Either set the `color_scheme` prop on the progress indicator " - "component or set the `accent_color` prop in your global theme." - ) - - color_scheme = color( - self.color_scheme if self.color_scheme is not None else global_color_scheme, # type: ignore - 9, - ) + if self.color_scheme is not None: + self.custom_attrs["data-accent-color"] = self.color_scheme self.style = Style( { - "background-color": color_scheme, + "background_color": color("accent", 9), "width": "100%", "height": "100%", f"transition": f"transform {DEFAULT_ANIMATION_DURATION}ms linear", @@ -87,6 +80,7 @@ class ProgressIndicator(ProgressComponent): }, "transform": f"translateX(calc(-100% + ({self.value} / {self.max} * 100%)))", # type: ignore "boxShadow": "inset 0 0 0 1px var(--gray-a5)", + **self.style, } ) @@ -94,43 +88,48 @@ class ProgressIndicator(ProgressComponent): return ["color_scheme"] -class Progress(ComponentNamespace): - """High-level API for progress bar.""" +class Progress(ProgressRoot): + """The high-level Progress component.""" - root = staticmethod(ProgressRoot.create) - indicator = staticmethod(ProgressIndicator.create) + # Override theme color for progress bar indicator + color_scheme: Var[LiteralAccentColor] - @staticmethod - def __call__( - width: Optional[str] = "100%", - color_scheme: Optional[Union[Var, LiteralAccentColor]] = None, - **props, - ) -> Component: + # The current progress value. + value: Var[Optional[int]] + + # The maximum progress value. + max: Var[Optional[int]] + + @classmethod + def create(cls, **props) -> Component: """High-level API for progress bar. Args: - width: The width of the progress bar. **props: The props of the progress bar. - color_scheme: The color scheme of the progress indicator. Returns: The progress bar. """ - style = props.setdefault("style", {}) - style.update({"width": width}) - - progress_indicator_props = ( - {"color_scheme": color_scheme} if color_scheme is not None else {} - ) + progress_indicator_props = {} + if "color_scheme" in props: + progress_indicator_props["color_scheme"] = props.pop("color_scheme") return ProgressRoot.create( ProgressIndicator.create( - value=props.get("value"), - max=props.get("max", 100), - **progress_indicator_props, # type: ignore + value=props.pop("value", 0), + max=props.pop("max", 100), + **progress_indicator_props, ), **props, ) -progress = Progress() +class ProgressNamespace(ComponentNamespace): + """High-level API for progress bar.""" + + root = staticmethod(ProgressRoot.create) + indicator = staticmethod(ProgressIndicator.create) + __call__ = staticmethod(Progress.create) + + +progress = ProgressNamespace() diff --git a/reflex/components/radix/primitives/progress.pyi b/reflex/components/radix/primitives/progress.pyi index 02c14ce17..98678588d 100644 --- a/reflex/components/radix/primitives/progress.pyi +++ b/reflex/components/radix/primitives/progress.pyi @@ -7,12 +7,12 @@ from typing import Any, Dict, Literal, Optional, Union, overload from reflex.vars import Var, BaseVar, ComputedVar from reflex.event import EventChain, EventHandler, EventSpec from reflex.style import Style -from typing import Optional, Union +from typing import Optional from reflex.components.component import Component, ComponentNamespace from reflex.components.core.colors import color from reflex.components.radix.primitives.accordion import DEFAULT_ANIMATION_DURATION from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName -from reflex.components.radix.themes.base import LiteralAccentColor +from reflex.components.radix.themes.base import LiteralAccentColor, LiteralRadius from reflex.style import Style from reflex.vars import Var @@ -103,8 +103,12 @@ class ProgressRoot(ProgressComponent): def create( # type: ignore cls, *children, - value: Optional[Union[Var[Optional[int]], Optional[int]]] = None, - max: Optional[Union[Var[int], int]] = None, + radius: Optional[ + Union[ + Var[Literal["none", "small", "medium", "large", "full"]], + Literal["none", "small", "medium", "large", "full"], + ] + ] = None, as_child: Optional[Union[Var[bool], bool]] = None, style: Optional[Style] = None, key: Optional[Any] = None, @@ -163,8 +167,7 @@ class ProgressRoot(ProgressComponent): Args: *children: The children of the component. - value: The current progress value. - max: The maximum progress value. + radius: Override theme radius for progress bar: "none" | "small" | "medium" | "large" | "full" 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. @@ -330,15 +333,307 @@ class ProgressIndicator(ProgressComponent): """ ... -class Progress(ComponentNamespace): +class Progress(ProgressRoot): + @overload + @classmethod + def create( # type: ignore + cls, + *children, + color_scheme: Optional[ + Union[ + Var[ + Literal[ + "tomato", + "red", + "ruby", + "crimson", + "pink", + "plum", + "purple", + "violet", + "iris", + "indigo", + "blue", + "cyan", + "teal", + "jade", + "green", + "grass", + "brown", + "orange", + "sky", + "mint", + "lime", + "yellow", + "amber", + "gold", + "bronze", + "gray", + ] + ], + Literal[ + "tomato", + "red", + "ruby", + "crimson", + "pink", + "plum", + "purple", + "violet", + "iris", + "indigo", + "blue", + "cyan", + "teal", + "jade", + "green", + "grass", + "brown", + "orange", + "sky", + "mint", + "lime", + "yellow", + "amber", + "gold", + "bronze", + "gray", + ], + ] + ] = None, + value: Optional[Union[Var[Optional[int]], Optional[int]]] = None, + max: Optional[Union[Var[Optional[int]], Optional[int]]] = None, + radius: Optional[ + Union[ + Var[Literal["none", "small", "medium", "large", "full"]], + Literal["none", "small", "medium", "large", "full"], + ] + ] = None, + as_child: 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, str]]] = None, + on_blur: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_click: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_context_menu: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_double_click: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_focus: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mount: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_down: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_enter: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_leave: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_move: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_out: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_over: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_up: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_scroll: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_unmount: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + **props + ) -> "Progress": + """High-level API for progress bar. + + Args: + color_scheme: Override theme color for progress bar indicator + value: The current progress value. + max: The maximum progress value. + radius: Override theme radius for progress bar: "none" | "small" | "medium" | "large" | "full" + 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 props of the progress bar. + + Returns: + The progress bar. + """ + ... + +class ProgressNamespace(ComponentNamespace): root = staticmethod(ProgressRoot.create) indicator = staticmethod(ProgressIndicator.create) @staticmethod def __call__( - width: Optional[str] = "100%", - color_scheme: Optional[Union[Var, LiteralAccentColor]] = None, + *children, + color_scheme: Optional[ + Union[ + Var[ + Literal[ + "tomato", + "red", + "ruby", + "crimson", + "pink", + "plum", + "purple", + "violet", + "iris", + "indigo", + "blue", + "cyan", + "teal", + "jade", + "green", + "grass", + "brown", + "orange", + "sky", + "mint", + "lime", + "yellow", + "amber", + "gold", + "bronze", + "gray", + ] + ], + Literal[ + "tomato", + "red", + "ruby", + "crimson", + "pink", + "plum", + "purple", + "violet", + "iris", + "indigo", + "blue", + "cyan", + "teal", + "jade", + "green", + "grass", + "brown", + "orange", + "sky", + "mint", + "lime", + "yellow", + "amber", + "gold", + "bronze", + "gray", + ], + ] + ] = None, + value: Optional[Union[Var[Optional[int]], Optional[int]]] = None, + max: Optional[Union[Var[Optional[int]], Optional[int]]] = None, + radius: Optional[ + Union[ + Var[Literal["none", "small", "medium", "large", "full"]], + Literal["none", "small", "medium", "large", "full"], + ] + ] = None, + as_child: 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, str]]] = None, + on_blur: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_click: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_context_menu: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_double_click: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_focus: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mount: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_down: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_enter: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_leave: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_move: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_out: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_over: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_up: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_scroll: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_unmount: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, **props - ) -> Component: ... + ) -> "Progress": + """High-level API for progress bar. -progress = Progress() + Args: + color_scheme: Override theme color for progress bar indicator + value: The current progress value. + max: The maximum progress value. + radius: Override theme radius for progress bar: "none" | "small" | "medium" | "large" | "full" + 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 props of the progress bar. + + Returns: + The progress bar. + """ + ... + +progress = ProgressNamespace()