[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
This commit is contained in:
Masen Furer 2024-03-05 12:07:20 -08:00 committed by GitHub
parent e4c32e3edf
commit 75b63cbc25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 348 additions and 54 deletions

View File

@ -2,13 +2,13 @@
from __future__ import annotations from __future__ import annotations
from typing import Optional, Union from typing import Optional
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
from reflex.components.radix.primitives.accordion import DEFAULT_ANIMATION_DURATION from reflex.components.radix.primitives.accordion import DEFAULT_ANIMATION_DURATION
from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName 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.style import Style
from reflex.vars import Var from reflex.vars import Var
@ -25,19 +25,19 @@ class ProgressRoot(ProgressComponent):
tag = "Root" tag = "Root"
alias = "RadixProgressRoot" alias = "RadixProgressRoot"
# The current progress value. # Override theme radius for progress bar: "none" | "small" | "medium" | "large" | "full"
value: Var[Optional[int]] radius: Var[LiteralRadius]
# The maximum progress value.
max: Var[int]
def _apply_theme(self, theme: Component): def _apply_theme(self, theme: Component):
if self.radius is not None:
self.custom_attrs["data-radius"] = self.radius
self.style = Style( self.style = Style(
{ {
"position": "relative", "position": "relative",
"overflow": "hidden", "overflow": "hidden",
"background": "var(--gray-a3)", "background": "var(--gray-a3)",
"border_radius": "99999px", "border_radius": "max(var(--radius-2), var(--radius-full))",
"width": "100%", "width": "100%",
"height": "20px", "height": "20px",
"boxShadow": "inset 0 0 0 1px var(--gray-a5)", "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): class ProgressIndicator(ProgressComponent):
"""The Progress bar indicator.""" """The Progress bar indicator."""
@ -63,22 +66,12 @@ class ProgressIndicator(ProgressComponent):
color_scheme: Var[LiteralAccentColor] color_scheme: Var[LiteralAccentColor]
def _apply_theme(self, theme: Component): def _apply_theme(self, theme: Component):
global_color_scheme = getattr(theme, "accent_color", None) if self.color_scheme is not None:
self.custom_attrs["data-accent-color"] = self.color_scheme
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,
)
self.style = Style( self.style = Style(
{ {
"background-color": color_scheme, "background_color": color("accent", 9),
"width": "100%", "width": "100%",
"height": "100%", "height": "100%",
f"transition": f"transform {DEFAULT_ANIMATION_DURATION}ms linear", 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 "transform": f"translateX(calc(-100% + ({self.value} / {self.max} * 100%)))", # type: ignore
"boxShadow": "inset 0 0 0 1px var(--gray-a5)", "boxShadow": "inset 0 0 0 1px var(--gray-a5)",
**self.style,
} }
) )
@ -94,43 +88,48 @@ class ProgressIndicator(ProgressComponent):
return ["color_scheme"] return ["color_scheme"]
class Progress(ComponentNamespace): class Progress(ProgressRoot):
"""High-level API for progress bar.""" """The high-level Progress component."""
root = staticmethod(ProgressRoot.create) # Override theme color for progress bar indicator
indicator = staticmethod(ProgressIndicator.create) color_scheme: Var[LiteralAccentColor]
@staticmethod # The current progress value.
def __call__( value: Var[Optional[int]]
width: Optional[str] = "100%",
color_scheme: Optional[Union[Var, LiteralAccentColor]] = None, # The maximum progress value.
**props, max: Var[Optional[int]]
) -> Component:
@classmethod
def create(cls, **props) -> Component:
"""High-level API for progress bar. """High-level API for progress bar.
Args: Args:
width: The width of the progress bar.
**props: The props of the progress bar. **props: The props of the progress bar.
color_scheme: The color scheme of the progress indicator.
Returns: Returns:
The progress bar. The progress bar.
""" """
style = props.setdefault("style", {}) progress_indicator_props = {}
style.update({"width": width}) if "color_scheme" in props:
progress_indicator_props["color_scheme"] = props.pop("color_scheme")
progress_indicator_props = (
{"color_scheme": color_scheme} if color_scheme is not None else {}
)
return ProgressRoot.create( return ProgressRoot.create(
ProgressIndicator.create( ProgressIndicator.create(
value=props.get("value"), value=props.pop("value", 0),
max=props.get("max", 100), max=props.pop("max", 100),
**progress_indicator_props, # type: ignore **progress_indicator_props,
), ),
**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()

View File

@ -7,12 +7,12 @@ from typing import Any, Dict, Literal, Optional, Union, overload
from reflex.vars import Var, BaseVar, ComputedVar from reflex.vars import Var, BaseVar, ComputedVar
from reflex.event import EventChain, EventHandler, EventSpec from reflex.event import EventChain, EventHandler, EventSpec
from reflex.style import Style from reflex.style import Style
from typing import Optional, Union from typing import Optional
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
from reflex.components.radix.primitives.accordion import DEFAULT_ANIMATION_DURATION from reflex.components.radix.primitives.accordion import DEFAULT_ANIMATION_DURATION
from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName 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.style import Style
from reflex.vars import Var from reflex.vars import Var
@ -103,8 +103,12 @@ class ProgressRoot(ProgressComponent):
def create( # type: ignore def create( # type: ignore
cls, cls,
*children, *children,
value: Optional[Union[Var[Optional[int]], Optional[int]]] = None, radius: Optional[
max: Optional[Union[Var[int], int]] = None, Union[
Var[Literal["none", "small", "medium", "large", "full"]],
Literal["none", "small", "medium", "large", "full"],
]
] = None,
as_child: Optional[Union[Var[bool], bool]] = None, as_child: Optional[Union[Var[bool], bool]] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
@ -163,8 +167,7 @@ class ProgressRoot(ProgressComponent):
Args: Args:
*children: The children of the component. *children: The children of the component.
value: The current progress value. radius: Override theme radius for progress bar: "none" | "small" | "medium" | "large" | "full"
max: The maximum progress value.
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.
@ -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) root = staticmethod(ProgressRoot.create)
indicator = staticmethod(ProgressIndicator.create) indicator = staticmethod(ProgressIndicator.create)
@staticmethod @staticmethod
def __call__( def __call__(
width: Optional[str] = "100%", *children,
color_scheme: Optional[Union[Var, LiteralAccentColor]] = None, 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 **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()