[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 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()

View File

@ -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()