Radix Accordion (#2310)
This commit is contained in:
parent
6fcc4fd357
commit
d466c2aaa2
@ -85,7 +85,7 @@
|
||||
{% macro render_match_tag(component) %}
|
||||
{
|
||||
(() => {
|
||||
switch (JSON.stringify({{ component.cond._var_full_name }})) {
|
||||
switch (JSON.stringify({{ component.cond._var_name_unwrapped }})) {
|
||||
{% for case in component.match_cases %}
|
||||
{% for condition in case[:-1] %}
|
||||
case JSON.stringify({{ condition._var_name_unwrapped }}):
|
||||
|
@ -623,6 +623,8 @@ class Component(BaseComponent, ABC):
|
||||
Returns:
|
||||
The dictionary of the component style as value and the style notation as key.
|
||||
"""
|
||||
if isinstance(self.style, Var):
|
||||
return {"css": self.style}
|
||||
return {"css": Var.create(format_as_emotion(self.style))}
|
||||
|
||||
def render(self) -> Dict:
|
||||
@ -721,7 +723,7 @@ class Component(BaseComponent, ABC):
|
||||
vars.append(prop_var)
|
||||
|
||||
# Style keeps track of its own VarData instance, so embed in a temp Var that is yielded.
|
||||
if self.style:
|
||||
if isinstance(self.style, dict) and self.style or isinstance(self.style, Var):
|
||||
vars.append(
|
||||
BaseVar(
|
||||
_var_name="style",
|
||||
|
@ -64,7 +64,8 @@ class Match(MemoizationLeaf):
|
||||
Raises:
|
||||
ValueError: If the condition is not provided.
|
||||
"""
|
||||
match_cond_var = Var.create(cond)
|
||||
match_cond_var = Var.create(cond, _var_is_string=type(cond) is str)
|
||||
|
||||
if match_cond_var is None:
|
||||
raise ValueError("The condition must be set")
|
||||
return match_cond_var # type: ignore
|
||||
@ -216,13 +217,14 @@ class Match(MemoizationLeaf):
|
||||
|
||||
return match_cond_var._replace(
|
||||
_var_name=format.format_match(
|
||||
cond=match_cond_var._var_full_name,
|
||||
cond=match_cond_var._var_name_unwrapped,
|
||||
match_cases=match_cases, # type: ignore
|
||||
default=default, # type: ignore
|
||||
),
|
||||
_var_type=default._var_type, # type: ignore
|
||||
_var_is_local=False,
|
||||
_var_full_name_needs_state_prefix=False,
|
||||
_var_is_string=False,
|
||||
merge_var_data=VarData.merge(*var_data),
|
||||
)
|
||||
|
||||
@ -247,11 +249,13 @@ class Match(MemoizationLeaf):
|
||||
for case in self.match_cases:
|
||||
if isinstance(case[-1], BaseComponent):
|
||||
merged_imports = imports.merge_imports(
|
||||
merged_imports, case[-1].get_imports()
|
||||
merged_imports,
|
||||
case[-1].get_imports(),
|
||||
)
|
||||
# Get the import of the default case component.
|
||||
if isinstance(self.default, BaseComponent):
|
||||
merged_imports = imports.merge_imports(
|
||||
merged_imports, self.default.get_imports()
|
||||
merged_imports,
|
||||
self.default.get_imports(),
|
||||
)
|
||||
return merged_imports
|
||||
|
@ -1,6 +1,12 @@
|
||||
"""Radix primitive components (https://www.radix-ui.com/primitives)."""
|
||||
|
||||
from .accordion import accordion, accordion_item
|
||||
from .accordion import (
|
||||
AccordionContent,
|
||||
AccordionHeader,
|
||||
AccordionRoot,
|
||||
AccordionTrigger,
|
||||
accordion_item,
|
||||
)
|
||||
from .form import (
|
||||
form_control,
|
||||
form_field,
|
||||
@ -12,3 +18,10 @@ from .form import (
|
||||
)
|
||||
from .progress import progress
|
||||
from .slider import slider
|
||||
|
||||
# accordion
|
||||
accordion = AccordionRoot.create
|
||||
accordion_root = AccordionRoot.create
|
||||
accordion_header = AccordionHeader.create
|
||||
accordion_trigger = AccordionTrigger.create
|
||||
accordion_content = AccordionContent.create
|
||||
|
@ -1,22 +1,370 @@
|
||||
"""Radix accordion components."""
|
||||
|
||||
from typing import Literal
|
||||
from typing import Any, Dict, Literal
|
||||
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.core import cond, match
|
||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
||||
from reflex.components.radix.themes.components.icons import Icon
|
||||
from reflex.style import Style
|
||||
from reflex.style import (
|
||||
Style,
|
||||
convert_dict_to_style_and_format_emotion,
|
||||
format_as_emotion,
|
||||
)
|
||||
from reflex.utils import imports
|
||||
from reflex.vars import Var
|
||||
from reflex.vars import BaseVar, Var
|
||||
|
||||
LiteralAccordionType = Literal["single", "multiple"]
|
||||
LiteralAccordionDir = Literal["ltr", "rtl"]
|
||||
LiteralAccordionOrientation = Literal["vertical", "horizontal"]
|
||||
|
||||
LiteralAccordionRootVariant = Literal["classic", "soft", "surface", "outline", "ghost"]
|
||||
LiteralAccordionRootColorScheme = Literal["primary", "accent"]
|
||||
|
||||
DEFAULT_ANIMATION_DURATION = 250
|
||||
|
||||
|
||||
def get_theme_accordion_root(variant: Var[str], color_scheme: Var[str]) -> BaseVar:
|
||||
"""Get the theme for the accordion root component.
|
||||
|
||||
Args:
|
||||
variant: The variant of the accordion.
|
||||
color_scheme: The color of the accordion.
|
||||
|
||||
Returns:
|
||||
The theme for the accordion root component.
|
||||
"""
|
||||
return match( # type: ignore
|
||||
variant,
|
||||
(
|
||||
"soft",
|
||||
convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"border_radius": "6px",
|
||||
"background_color": cond(
|
||||
color_scheme == "primary", "var(--accent-3)", "var(--slate-3)"
|
||||
),
|
||||
"box_shadow": "0 2px 10px var(--black-a1)",
|
||||
}
|
||||
),
|
||||
),
|
||||
(
|
||||
"outline",
|
||||
convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"border_radius": "6px",
|
||||
"border": cond(
|
||||
color_scheme == "primary",
|
||||
"1px solid var(--accent-6)",
|
||||
"1px solid var(--slate-6)",
|
||||
),
|
||||
"box_shadow": "0 2px 10px var(--black-a1)",
|
||||
}
|
||||
),
|
||||
),
|
||||
(
|
||||
"surface",
|
||||
convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"border_radius": "6px",
|
||||
"border": cond(
|
||||
color_scheme == "primary",
|
||||
"1px solid var(--accent-6)",
|
||||
"1px solid var(--slate-6)",
|
||||
),
|
||||
"background_color": cond(
|
||||
color_scheme == "primary", "var(--accent-3)", "var(--slate-3)"
|
||||
),
|
||||
"box_shadow": "0 2px 10px var(--black-a1)",
|
||||
}
|
||||
),
|
||||
),
|
||||
(
|
||||
"ghost",
|
||||
convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"border_radius": "6px",
|
||||
"background_color": "none",
|
||||
"box_shadow": "None",
|
||||
}
|
||||
),
|
||||
),
|
||||
convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"border_radius": "6px",
|
||||
"background_color": cond(
|
||||
color_scheme == "primary", "var(--accent-9)", "var(--slate-9)"
|
||||
),
|
||||
"box_shadow": "0 2px 10px var(--black-a4)",
|
||||
}
|
||||
)
|
||||
# defaults to classic
|
||||
)
|
||||
|
||||
|
||||
def get_theme_accordion_item():
|
||||
"""Get the theme for the accordion item component.
|
||||
|
||||
Returns:
|
||||
The theme for the accordion item component.
|
||||
"""
|
||||
return convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"overflow": "hidden",
|
||||
"width": "100%",
|
||||
"margin_top": "1px",
|
||||
# "background_color": "var(--accent-3)",
|
||||
# "background_color": cond(
|
||||
# color_scheme == "primary", "var(--accent-3)", "var(--slate-3)"
|
||||
# ),
|
||||
"&:first-child": {
|
||||
"margin_top": 0,
|
||||
"border_top_left_radius": "4px",
|
||||
"border_top_right_radius": "4px",
|
||||
},
|
||||
"&:last-child": {
|
||||
"border_bottom_left_radius": "4px",
|
||||
"border_bottom_right_radius": "4px",
|
||||
},
|
||||
"&:focus-within": {
|
||||
"position": "relative",
|
||||
"z_index": 1,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def get_theme_accordion_header() -> dict[str, str]:
|
||||
"""Get the theme for the accordion header component.
|
||||
|
||||
Returns:
|
||||
The theme for the accordion header component.
|
||||
"""
|
||||
return {
|
||||
"display": "flex",
|
||||
}
|
||||
|
||||
|
||||
def get_theme_accordion_trigger(variant: str | Var, color_scheme: str | Var) -> BaseVar:
|
||||
"""Get the theme for the accordion trigger component.
|
||||
|
||||
Args:
|
||||
variant: The variant of the accordion.
|
||||
color_scheme: The color of the accordion.
|
||||
|
||||
Returns:
|
||||
The theme for the accordion trigger component.
|
||||
"""
|
||||
return match( # type: ignore
|
||||
variant,
|
||||
(
|
||||
"soft",
|
||||
convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"color": cond(
|
||||
color_scheme == "primary",
|
||||
"var(--accent-9-contrast)",
|
||||
"var(--slate-9-contrast)",
|
||||
),
|
||||
"&:hover": {
|
||||
"background_color": cond(
|
||||
color_scheme == "primary",
|
||||
"var(--accent-4)",
|
||||
"var(--slate-4)",
|
||||
),
|
||||
},
|
||||
"& > .AccordionChevron": {
|
||||
"color": cond(
|
||||
color_scheme == "primary",
|
||||
"var(--accent-11)",
|
||||
"var(--slate-11)",
|
||||
),
|
||||
"transition": f"transform {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
|
||||
},
|
||||
"&[data-state='open'] > .AccordionChevron": {
|
||||
"transform": "rotate(180deg)",
|
||||
},
|
||||
"font_family": "inherit",
|
||||
"width": "100%",
|
||||
"padding": "0 20px",
|
||||
"height": "45px",
|
||||
"flex": 1,
|
||||
"display": "flex",
|
||||
"align_items": "center",
|
||||
"justify_content": "space-between",
|
||||
"font_size": "15px",
|
||||
"box_shadow": "0 1px 0 var(--accent-6)",
|
||||
"line_height": 1,
|
||||
}
|
||||
),
|
||||
),
|
||||
(
|
||||
"outline",
|
||||
"surface",
|
||||
"ghost",
|
||||
convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"color": cond(
|
||||
color_scheme == "primary",
|
||||
"var(--accent-11)",
|
||||
"var(--slate-11)",
|
||||
),
|
||||
"&:hover": {
|
||||
"background_color": cond(
|
||||
color_scheme == "primary",
|
||||
"var(--accent-4)",
|
||||
"var(--slate-4)",
|
||||
),
|
||||
},
|
||||
"& > .AccordionChevron": {
|
||||
"color": cond(
|
||||
color_scheme == "primary",
|
||||
"var(--accent-11)",
|
||||
"var(--slate-11)",
|
||||
),
|
||||
"transition": f"transform {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
|
||||
},
|
||||
"&[data-state='open'] > .AccordionChevron": {
|
||||
"transform": "rotate(180deg)",
|
||||
},
|
||||
"font_family": "inherit",
|
||||
"width": "100%",
|
||||
"padding": "0 20px",
|
||||
"height": "45px",
|
||||
"flex": 1,
|
||||
"display": "flex",
|
||||
"align_items": "center",
|
||||
"justify_content": "space-between",
|
||||
"font_size": "15px",
|
||||
"box_shadow": "0 1px 0 var(--accent-6)",
|
||||
"line_height": 1,
|
||||
}
|
||||
),
|
||||
),
|
||||
# defaults to classic
|
||||
convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"color": cond(
|
||||
color_scheme == "primary",
|
||||
"var(--accent-9-contrast)",
|
||||
"var(--slate-9-contrast)",
|
||||
),
|
||||
"box_shadow": "0 1px 0 var(--accent-6)",
|
||||
"&:hover": {
|
||||
"background_color": cond(
|
||||
color_scheme == "primary", "var(--accent-10)", "var(--slate-10)"
|
||||
),
|
||||
},
|
||||
"& > .AccordionChevron": {
|
||||
"color": cond(
|
||||
color_scheme == "primary",
|
||||
"var(--accent-9-contrast)",
|
||||
"var(--slate-9-contrast)",
|
||||
),
|
||||
"transition": f"transform {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
|
||||
},
|
||||
"&[data-state='open'] > .AccordionChevron": {
|
||||
"transform": "rotate(180deg)",
|
||||
},
|
||||
"font_family": "inherit",
|
||||
"width": "100%",
|
||||
"padding": "0 20px",
|
||||
"height": "45px",
|
||||
"flex": 1,
|
||||
"display": "flex",
|
||||
"align_items": "center",
|
||||
"justify_content": "space-between",
|
||||
"font_size": "15px",
|
||||
"line_height": 1,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def get_theme_accordion_content(variant: str | Var, color_scheme: str | Var) -> BaseVar:
|
||||
"""Get the theme for the accordion content component.
|
||||
|
||||
Args:
|
||||
variant: The variant of the accordion.
|
||||
color_scheme: The color of the accordion.
|
||||
|
||||
Returns:
|
||||
The theme for the accordion content component.
|
||||
"""
|
||||
return match( # type: ignore
|
||||
variant,
|
||||
(
|
||||
"outline",
|
||||
"ghost",
|
||||
convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"overflow": "hidden",
|
||||
"font_size": "10px",
|
||||
"color": cond(
|
||||
color_scheme == "primary",
|
||||
"var(--accent-9-contrast)",
|
||||
"var(--slate-9-contrast)",
|
||||
),
|
||||
"background_color": cond(
|
||||
color_scheme == "primary", "var(--accent-3)", "var(--slate-3)"
|
||||
),
|
||||
"padding": "15px, 20px",
|
||||
"&[data-state='open']": {
|
||||
"animation": Var.create(
|
||||
f"${{slideDown}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
|
||||
_var_is_string=True,
|
||||
),
|
||||
},
|
||||
"&[data-state='closed']": {
|
||||
"animation": Var.create(
|
||||
f"${{slideUp}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
|
||||
_var_is_string=True,
|
||||
),
|
||||
},
|
||||
}
|
||||
),
|
||||
),
|
||||
convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"overflow": "hidden",
|
||||
"font_size": "10px",
|
||||
"color": cond(
|
||||
color_scheme == "primary",
|
||||
"var(--accent-9-contrast)",
|
||||
"var(--slate-9-contrast)",
|
||||
),
|
||||
"background_color": match(
|
||||
variant,
|
||||
(
|
||||
"classic",
|
||||
cond(
|
||||
color_scheme == "primary",
|
||||
"var(--accent-9)",
|
||||
"var(--slate-9)",
|
||||
),
|
||||
),
|
||||
cond(
|
||||
color_scheme == "primary", "var(--accent-3)", "var(--slate-3)"
|
||||
),
|
||||
),
|
||||
"padding": "15px, 20px",
|
||||
"&[data-state='open']": {
|
||||
"animation": Var.create(
|
||||
f"${{slideDown}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
|
||||
_var_is_string=True,
|
||||
),
|
||||
},
|
||||
"&[data-state='closed']": {
|
||||
"animation": Var.create(
|
||||
f"${{slideUp}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
|
||||
_var_is_string=True,
|
||||
),
|
||||
},
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class AccordionComponent(RadixPrimitiveComponent):
|
||||
"""Base class for all @radix-ui/accordion components."""
|
||||
|
||||
@ -51,16 +399,79 @@ class AccordionRoot(AccordionComponent):
|
||||
# The orientation of the accordion.
|
||||
orientation: Var[LiteralAccordionOrientation]
|
||||
|
||||
# The variant of the accordion.
|
||||
variant: Var[LiteralAccordionRootVariant] = "classic" # type: ignore
|
||||
|
||||
# The color scheme of the accordion.
|
||||
color_scheme: Var[LiteralAccordionRootColorScheme] = "primary" # type: ignore
|
||||
|
||||
# dynamic themes of the accordion generated at compile time.
|
||||
_dynamic_themes: Var[dict]
|
||||
|
||||
@classmethod
|
||||
def create(cls, *children, **props) -> Component:
|
||||
"""Create the Accordion root component.
|
||||
|
||||
Args:
|
||||
*children: The children of the component.
|
||||
**props: The properties of the component.
|
||||
|
||||
Returns:
|
||||
The Accordion root Component.
|
||||
"""
|
||||
comp = super().create(*children, **props)
|
||||
|
||||
if not comp.color_scheme._var_state: # type: ignore
|
||||
# mark the vars of color string literals as strings so they can be formatted properly when performing a var operation.
|
||||
comp.color_scheme._var_is_string = True # type: ignore
|
||||
|
||||
if not comp.variant._var_state: # type: ignore
|
||||
# mark the vars of variant string literals as strings so they are formatted properly in the match condition.
|
||||
comp.variant._var_is_string = True # type: ignore
|
||||
|
||||
# remove Fragment and cond wrap workaround when https://github.com/reflex-dev/reflex/issues/2393 is resolved.
|
||||
return Fragment.create(comp, cond(True, Fragment.create()))
|
||||
|
||||
def _get_style(self) -> dict:
|
||||
"""Get the style for the component.
|
||||
|
||||
Returns:
|
||||
The dictionary of the component style as value and the style notation as key.
|
||||
"""
|
||||
return {"css": self._dynamic_themes._merge(format_as_emotion(self.style))} # type: ignore
|
||||
|
||||
def _apply_theme(self, theme: Component):
|
||||
self.style = Style(
|
||||
{
|
||||
"border_radius": "6px",
|
||||
"background_color": "var(--accent-6)",
|
||||
"box_shadow": "0 2px 10px var(--black-a4)",
|
||||
**self.style,
|
||||
}
|
||||
|
||||
self._dynamic_themes = Var.create( # type: ignore
|
||||
convert_dict_to_style_and_format_emotion(
|
||||
{
|
||||
"& .AccordionItem": get_theme_accordion_item(),
|
||||
"& .AccordionHeader": get_theme_accordion_header(),
|
||||
"& .AccordionTrigger": get_theme_accordion_trigger(
|
||||
variant=self.variant, color_scheme=self.color_scheme
|
||||
),
|
||||
"& .AccordionContent": get_theme_accordion_content(
|
||||
variant=self.variant, color_scheme=self.color_scheme
|
||||
),
|
||||
}
|
||||
)
|
||||
)._merge( # type: ignore
|
||||
get_theme_accordion_root(
|
||||
variant=self.variant, color_scheme=self.color_scheme
|
||||
)
|
||||
)
|
||||
|
||||
def get_event_triggers(self) -> Dict[str, Any]:
|
||||
"""Get the events triggers signatures for the component.
|
||||
|
||||
Returns:
|
||||
The signatures of the event triggers.
|
||||
"""
|
||||
return {
|
||||
**super().get_event_triggers(),
|
||||
"on_value_change": lambda e0: [e0],
|
||||
}
|
||||
|
||||
|
||||
class AccordionItem(AccordionComponent):
|
||||
"""An accordion component."""
|
||||
@ -78,22 +489,6 @@ class AccordionItem(AccordionComponent):
|
||||
def _apply_theme(self, theme: Component):
|
||||
self.style = Style(
|
||||
{
|
||||
"overflow": "hidden",
|
||||
"margin_top": "1px",
|
||||
"&:first-child": {
|
||||
"margin_top": 0,
|
||||
"border_top_left_radius": "4px",
|
||||
"border_top_right_radius": "4px",
|
||||
},
|
||||
"&:last-child": {
|
||||
"border_bottom_left_radius": "4px",
|
||||
"border_bottom_right_radius": "4px",
|
||||
},
|
||||
"&:focus-within": {
|
||||
"position": "relative",
|
||||
"z_index": 1,
|
||||
"box_shadow": "0 0 0 2px var(--accent-7)",
|
||||
},
|
||||
**self.style,
|
||||
}
|
||||
)
|
||||
@ -109,7 +504,6 @@ class AccordionHeader(AccordionComponent):
|
||||
def _apply_theme(self, theme: Component):
|
||||
self.style = Style(
|
||||
{
|
||||
"display": "flex",
|
||||
**self.style,
|
||||
}
|
||||
)
|
||||
@ -125,27 +519,6 @@ class AccordionTrigger(AccordionComponent):
|
||||
def _apply_theme(self, theme: Component):
|
||||
self.style = Style(
|
||||
{
|
||||
"font_family": "inherit",
|
||||
"padding": "0 20px",
|
||||
"height": "45px",
|
||||
"flex": 1,
|
||||
"display": "flex",
|
||||
"align_items": "center",
|
||||
"justify_content": "space-between",
|
||||
"font_size": "15px",
|
||||
"line_height": 1,
|
||||
"color": "var(--accent-11)",
|
||||
"box_shadow": "0 1px 0 var(--accent-6)",
|
||||
"&:hover": {
|
||||
"background_color": "var(--gray-2)",
|
||||
},
|
||||
"& > .AccordionChevron": {
|
||||
"color": "var(--accent-10)",
|
||||
"transition": f"transform {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
|
||||
},
|
||||
"&[data-state='open'] > .AccordionChevron": {
|
||||
"transform": "rotate(180deg)",
|
||||
},
|
||||
**self.style,
|
||||
}
|
||||
)
|
||||
@ -161,23 +534,6 @@ class AccordionContent(AccordionComponent):
|
||||
def _apply_theme(self, theme: Component):
|
||||
self.style = Style(
|
||||
{
|
||||
"overflow": "hidden",
|
||||
"fontSize": "15px",
|
||||
"color": "var(--accent-11)",
|
||||
"backgroundColor": "var(--accent-2)",
|
||||
"padding": "15px, 20px",
|
||||
"&[data-state='open']": {
|
||||
"animation": Var.create(
|
||||
f"${{slideDown}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
|
||||
_var_is_string=True,
|
||||
),
|
||||
},
|
||||
"&[data-state='closed']": {
|
||||
"animation": Var.create(
|
||||
f"${{slideUp}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
|
||||
_var_is_string=True,
|
||||
),
|
||||
},
|
||||
**self.style,
|
||||
}
|
||||
)
|
||||
@ -231,14 +587,14 @@ def accordion_item(header: Component, content: Component, **props) -> Component:
|
||||
tag="chevron_down",
|
||||
class_name="AccordionChevron",
|
||||
),
|
||||
class_name="AccordionTrigger",
|
||||
),
|
||||
),
|
||||
AccordionContent.create(
|
||||
content,
|
||||
class_name="AccordionContent",
|
||||
),
|
||||
value=value,
|
||||
**props,
|
||||
class_name="AccordionItem",
|
||||
)
|
||||
|
||||
|
||||
accordion = AccordionRoot.create
|
||||
|
@ -7,19 +7,37 @@ 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 Literal
|
||||
from typing import Any, Dict, Literal
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.core import cond, match
|
||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
||||
from reflex.components.radix.themes.components.icons import Icon
|
||||
from reflex.style import Style
|
||||
from reflex.style import (
|
||||
Style,
|
||||
convert_dict_to_style_and_format_emotion,
|
||||
format_as_emotion,
|
||||
)
|
||||
from reflex.utils import imports
|
||||
from reflex.vars import Var
|
||||
from reflex.vars import BaseVar, Var
|
||||
|
||||
LiteralAccordionType = Literal["single", "multiple"]
|
||||
LiteralAccordionDir = Literal["ltr", "rtl"]
|
||||
LiteralAccordionOrientation = Literal["vertical", "horizontal"]
|
||||
LiteralAccordionRootVariant = Literal["classic", "soft", "surface", "outline", "ghost"]
|
||||
LiteralAccordionRootColorScheme = Literal["primary", "accent"]
|
||||
DEFAULT_ANIMATION_DURATION = 250
|
||||
|
||||
def get_theme_accordion_root(variant: Var[str], color_scheme: Var[str]) -> BaseVar: ...
|
||||
def get_theme_accordion_item(): ...
|
||||
def get_theme_accordion_header() -> dict[str, str]: ...
|
||||
def get_theme_accordion_trigger(
|
||||
variant: str | Var, color_scheme: str | Var
|
||||
) -> BaseVar: ...
|
||||
def get_theme_accordion_content(
|
||||
variant: str | Var, color_scheme: str | Var
|
||||
) -> BaseVar: ...
|
||||
|
||||
class AccordionComponent(RadixPrimitiveComponent):
|
||||
@overload
|
||||
@classmethod
|
||||
@ -121,6 +139,16 @@ class AccordionRoot(AccordionComponent):
|
||||
Literal["vertical", "horizontal"],
|
||||
]
|
||||
] = None,
|
||||
variant: Optional[
|
||||
Union[
|
||||
Var[Literal["classic", "soft", "surface", "outline", "ghost"]],
|
||||
Literal["classic", "soft", "surface", "outline", "ghost"],
|
||||
]
|
||||
] = None,
|
||||
color_scheme: Optional[
|
||||
Union[Var[Literal["primary", "accent"]], Literal["primary", "accent"]]
|
||||
] = None,
|
||||
_dynamic_themes: Optional[Union[Var[dict], dict]] = None,
|
||||
as_child: Optional[Union[Var[bool], bool]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
@ -173,9 +201,12 @@ class AccordionRoot(AccordionComponent):
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
on_value_change: Optional[
|
||||
Union[EventHandler, EventSpec, list, function, BaseVar]
|
||||
] = None,
|
||||
**props
|
||||
) -> "AccordionRoot":
|
||||
"""Create the component.
|
||||
"""Create the Accordion root component.
|
||||
|
||||
Args:
|
||||
*children: The children of the component.
|
||||
@ -186,6 +217,9 @@ class AccordionRoot(AccordionComponent):
|
||||
disabled: Whether or not the accordion is disabled.
|
||||
dir: The reading direction of the accordion when applicable.
|
||||
orientation: The orientation of the accordion.
|
||||
variant: The variant of the accordion.
|
||||
color_scheme: The color scheme of the accordion.
|
||||
_dynamic_themes: dynamic themes of the accordion generated at compile time.
|
||||
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.
|
||||
@ -193,15 +227,13 @@ class AccordionRoot(AccordionComponent):
|
||||
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 component.
|
||||
**props: The properties of the component.
|
||||
|
||||
Returns:
|
||||
The component.
|
||||
|
||||
Raises:
|
||||
TypeError: If an invalid child is passed.
|
||||
The Accordion root Component.
|
||||
"""
|
||||
...
|
||||
def get_event_triggers(self) -> Dict[str, Any]: ...
|
||||
|
||||
class AccordionItem(AccordionComponent):
|
||||
@overload
|
||||
@ -532,5 +564,3 @@ class AccordionContent(AccordionComponent):
|
||||
...
|
||||
|
||||
def accordion_item(header: Component, content: Component, **props) -> Component: ...
|
||||
|
||||
accordion = AccordionRoot.create
|
||||
|
@ -15,6 +15,10 @@ class RadixPrimitiveComponent(Component):
|
||||
|
||||
lib_dependencies: List[str] = ["@emotion/react@^11.11.1"]
|
||||
|
||||
|
||||
class RadixPrimitiveComponentWithClassName(RadixPrimitiveComponent):
|
||||
"""Basic component for radix Primitives with a class name prop."""
|
||||
|
||||
def _render(self) -> Tag:
|
||||
return (
|
||||
super()
|
||||
|
@ -93,3 +93,84 @@ class RadixPrimitiveComponent(Component):
|
||||
TypeError: If an invalid child is passed.
|
||||
"""
|
||||
...
|
||||
|
||||
class RadixPrimitiveComponentWithClassName(RadixPrimitiveComponent):
|
||||
@overload
|
||||
@classmethod
|
||||
def create( # type: ignore
|
||||
cls,
|
||||
*children,
|
||||
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
|
||||
) -> "RadixPrimitiveComponentWithClassName":
|
||||
"""Create the component.
|
||||
|
||||
Args:
|
||||
*children: The children of the component.
|
||||
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 component.
|
||||
|
||||
Returns:
|
||||
The component.
|
||||
|
||||
Raises:
|
||||
TypeError: If an invalid child is passed.
|
||||
"""
|
||||
...
|
||||
|
@ -14,7 +14,7 @@ from reflex.utils import imports
|
||||
from reflex.utils.format import format_event_chain, to_camel_case
|
||||
from reflex.vars import BaseVar, Var
|
||||
|
||||
from .base import RadixPrimitiveComponent
|
||||
from .base import RadixPrimitiveComponentWithClassName
|
||||
|
||||
FORM_DATA = Var.create("form_data")
|
||||
HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
|
||||
@ -34,7 +34,7 @@ HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
|
||||
)
|
||||
|
||||
|
||||
class FormComponent(RadixPrimitiveComponent):
|
||||
class FormComponent(RadixPrimitiveComponentWithClassName):
|
||||
"""Base class for all @radix-ui/react-form components."""
|
||||
|
||||
library = "@radix-ui/react-form@^0.0.3"
|
||||
|
@ -18,14 +18,14 @@ from reflex.event import EventChain
|
||||
from reflex.utils import imports
|
||||
from reflex.utils.format import format_event_chain, to_camel_case
|
||||
from reflex.vars import BaseVar, Var
|
||||
from .base import RadixPrimitiveComponent
|
||||
from .base import RadixPrimitiveComponentWithClassName
|
||||
|
||||
FORM_DATA = Var.create("form_data")
|
||||
HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
|
||||
"\n const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {\n const $form = ev.target\n ev.preventDefault()\n const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}}\n\n {{ on_submit_event_chain }}\n\n if ({{ reset_on_submit }}) {\n $form.reset()\n }\n })\n "
|
||||
)
|
||||
|
||||
class FormComponent(RadixPrimitiveComponent):
|
||||
class FormComponent(RadixPrimitiveComponentWithClassName):
|
||||
@overload
|
||||
@classmethod
|
||||
def create( # type: ignore
|
||||
|
@ -3,12 +3,12 @@
|
||||
from typing import Optional
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
|
||||
|
||||
class ProgressComponent(RadixPrimitiveComponent):
|
||||
class ProgressComponent(RadixPrimitiveComponentWithClassName):
|
||||
"""A Progress component."""
|
||||
|
||||
library = "@radix-ui/react-progress@^1.0.3"
|
||||
|
@ -9,11 +9,11 @@ from reflex.event import EventChain, EventHandler, EventSpec
|
||||
from reflex.style import Style
|
||||
from typing import Optional
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
|
||||
class ProgressComponent(RadixPrimitiveComponent):
|
||||
class ProgressComponent(RadixPrimitiveComponentWithClassName):
|
||||
@overload
|
||||
@classmethod
|
||||
def create( # type: ignore
|
||||
|
@ -3,7 +3,7 @@
|
||||
from typing import Any, Dict, Literal
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
|
||||
@ -11,7 +11,7 @@ LiteralSliderOrientation = Literal["horizontal", "vertical"]
|
||||
LiteralSliderDir = Literal["ltr", "rtl"]
|
||||
|
||||
|
||||
class SliderComponent(RadixPrimitiveComponent):
|
||||
class SliderComponent(RadixPrimitiveComponentWithClassName):
|
||||
"""Base class for all @radix-ui/react-slider components."""
|
||||
|
||||
library = "@radix-ui/react-slider@^1.1.2"
|
||||
|
@ -9,14 +9,14 @@ from reflex.event import EventChain, EventHandler, EventSpec
|
||||
from reflex.style import Style
|
||||
from typing import Any, Dict, Literal
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
|
||||
LiteralSliderOrientation = Literal["horizontal", "vertical"]
|
||||
LiteralSliderDir = Literal["ltr", "rtl"]
|
||||
|
||||
class SliderComponent(RadixPrimitiveComponent):
|
||||
class SliderComponent(RadixPrimitiveComponentWithClassName):
|
||||
@overload
|
||||
@classmethod
|
||||
def create( # type: ignore
|
||||
|
@ -46,6 +46,7 @@ class Icon(RadixIconComponent):
|
||||
f"Invalid icon tag: {props['tag']}. Please use one of the following: {sorted(ICON_LIST)}"
|
||||
)
|
||||
props["tag"] = format.to_title_case(props["tag"]) + "Icon"
|
||||
props["alias"] = f"RadixThemes{props['tag']}"
|
||||
return super().create(*children, **props)
|
||||
|
||||
|
||||
|
@ -220,3 +220,18 @@ def format_as_emotion(style_dict: dict[str, Any]) -> dict[str, Any] | None:
|
||||
emotion_style[key] = value
|
||||
if emotion_style:
|
||||
return emotion_style
|
||||
|
||||
|
||||
def convert_dict_to_style_and_format_emotion(
|
||||
raw_dict: dict[str, Any]
|
||||
) -> dict[str, Any] | None:
|
||||
"""Convert a dict to a style dict and then format as emotion.
|
||||
|
||||
Args:
|
||||
raw_dict: The dict to convert.
|
||||
|
||||
Returns:
|
||||
The emotion dict.
|
||||
|
||||
"""
|
||||
return format_as_emotion(Style(raw_dict))
|
||||
|
@ -421,6 +421,26 @@ class Var:
|
||||
and self._var_data == other._var_data
|
||||
)
|
||||
|
||||
def _merge(self, other) -> Var:
|
||||
"""Merge two or more dicts.
|
||||
|
||||
Args:
|
||||
other: The other var to merge.
|
||||
|
||||
Returns:
|
||||
The merged var.
|
||||
|
||||
Raises:
|
||||
ValueError: If the other value to be merged is None.
|
||||
"""
|
||||
if other is None:
|
||||
raise ValueError("The value to be merged cannot be None.")
|
||||
if not isinstance(other, Var):
|
||||
other = Var.create(other)
|
||||
return self._replace(
|
||||
_var_name=f"{{...{self._var_name}, ...{other._var_name}}}" # type: ignore
|
||||
)
|
||||
|
||||
def to_string(self, json: bool = True) -> Var:
|
||||
"""Convert a var to a string.
|
||||
|
||||
@ -677,6 +697,16 @@ class Var:
|
||||
|
||||
left_operand, right_operand = (other, self) if flip else (self, other)
|
||||
|
||||
def get_operand_full_name(operand):
|
||||
# operand vars that are string literals need to be wrapped in back ticks.
|
||||
return (
|
||||
operand._var_name_unwrapped
|
||||
if operand._var_is_string
|
||||
and not operand._var_state
|
||||
and operand._var_is_local
|
||||
else operand._var_full_name
|
||||
)
|
||||
|
||||
if other is not None:
|
||||
# check if the operation between operands is valid.
|
||||
if op and not self.is_valid_operation(
|
||||
@ -688,18 +718,22 @@ class Var:
|
||||
f"Unsupported Operand type(s) for {op}: `{left_operand._var_full_name}` of type {left_operand._var_type.__name__} and `{right_operand._var_full_name}` of type {right_operand._var_type.__name__}" # type: ignore
|
||||
)
|
||||
|
||||
left_operand_full_name = get_operand_full_name(left_operand)
|
||||
right_operand_full_name = get_operand_full_name(right_operand)
|
||||
|
||||
# apply function to operands
|
||||
if fn is not None:
|
||||
|
||||
if invoke_fn:
|
||||
# invoke the function on left operand.
|
||||
operation_name = f"{left_operand._var_full_name}.{fn}({right_operand._var_full_name})" # type: ignore
|
||||
operation_name = f"{left_operand_full_name}.{fn}({right_operand_full_name})" # type: ignore
|
||||
else:
|
||||
# pass the operands as arguments to the function.
|
||||
operation_name = f"{left_operand._var_full_name} {op} {right_operand._var_full_name}" # type: ignore
|
||||
operation_name = f"{left_operand_full_name} {op} {right_operand_full_name}" # type: ignore
|
||||
operation_name = f"{fn}({operation_name})"
|
||||
else:
|
||||
# apply operator to operands (left operand <operator> right_operand)
|
||||
operation_name = f"{left_operand._var_full_name} {op} {right_operand._var_full_name}" # type: ignore
|
||||
operation_name = f"{left_operand_full_name} {op} {right_operand_full_name}" # type: ignore
|
||||
operation_name = format.wrap(operation_name, "(")
|
||||
else:
|
||||
# apply operator to left operand (<operator> left_operand)
|
||||
|
@ -262,6 +262,41 @@ def test_basic_operations(TestObj):
|
||||
assert str(v(1) | v(2)) == "{(1 || 2)}"
|
||||
assert str(v([1, 2, 3])[v(0)]) == "{[1, 2, 3].at(0)}"
|
||||
assert str(v({"a": 1, "b": 2})["a"]) == '{{"a": 1, "b": 2}["a"]}'
|
||||
assert str(v("foo") == v("bar")) == '{("foo" === "bar")}'
|
||||
assert (
|
||||
str(
|
||||
Var.create("foo", _var_is_local=False)
|
||||
== Var.create("bar", _var_is_local=False)
|
||||
)
|
||||
== "{(foo === bar)}"
|
||||
)
|
||||
assert (
|
||||
str(
|
||||
BaseVar(
|
||||
_var_name="foo", _var_type=str, _var_is_string=True, _var_is_local=True
|
||||
)
|
||||
== BaseVar(
|
||||
_var_name="bar", _var_type=str, _var_is_string=True, _var_is_local=True
|
||||
)
|
||||
)
|
||||
== "(`foo` === `bar`)"
|
||||
)
|
||||
assert (
|
||||
str(
|
||||
BaseVar(
|
||||
_var_name="foo",
|
||||
_var_type=TestObj,
|
||||
_var_is_string=True,
|
||||
_var_is_local=False,
|
||||
)
|
||||
._var_set_state("state")
|
||||
.bar
|
||||
== BaseVar(
|
||||
_var_name="bar", _var_type=str, _var_is_string=True, _var_is_local=True
|
||||
)
|
||||
)
|
||||
== "{(state.foo.bar === `bar`)}"
|
||||
)
|
||||
assert (
|
||||
str(BaseVar(_var_name="foo", _var_type=TestObj)._var_set_state("state").bar)
|
||||
== "{state.foo.bar}"
|
||||
|
Loading…
Reference in New Issue
Block a user