浏览代码

Implement accordion component (#2262)

* Implement accordion component

* Fix literal annotation

* Use style prop instead of css

* Move accordion to primitives folder

* Add pyi file

* Fix pyright

* Add apply_theme method

* Fix apply_theme

* Use apply_theme method

* Fix lint
Nikhil Rao 1 年之前
父节点
当前提交
96f68cb94d

+ 2 - 0
reflex/.templates/jinja/web/pages/base_page.js.jinja2

@@ -1,5 +1,7 @@
 {% import 'web/pages/utils.js.jinja2' as utils %}
 {% import 'web/pages/utils.js.jinja2' as utils %}
 
 
+/** @jsxImportSource @emotion/react */
+
 {%- block imports_libs %}
 {%- block imports_libs %}
 {% for module in imports%}
 {% for module in imports%}
   {{- utils.get_import(module) }}
   {{- utils.get_import(module) }}

+ 3 - 0
reflex/app.py

@@ -667,6 +667,9 @@ class App(Base):
                 # Merge the component style with the app style.
                 # Merge the component style with the app style.
                 component.add_style(self.style)
                 component.add_style(self.style)
 
 
+                if self.theme is not None:
+                    component.apply_theme(self.theme)
+
                 # Add component.get_imports() to all_imports.
                 # Add component.get_imports() to all_imports.
                 all_imports.update(component.get_imports())
                 all_imports.update(component.get_imports())
 
 

+ 22 - 1
reflex/components/component.py

@@ -288,6 +288,7 @@ class Component(BaseComponent, ABC):
 
 
         kwargs["style"] = Style(
         kwargs["style"] = Style(
             {
             {
+                **self.get_fields()["style"].default,
                 **style,
                 **style,
                 **{attr: value for attr, value in kwargs.items() if attr not in fields},
                 **{attr: value for attr, value in kwargs.items() if attr not in fields},
             }
             }
@@ -445,6 +446,26 @@ class Component(BaseComponent, ABC):
 
 
         return _compile_component(self)
         return _compile_component(self)
 
 
+    def _apply_theme(self, theme: Component):
+        """Apply the theme to this component.
+
+        Args:
+            theme: The theme to apply.
+        """
+        pass
+
+    def apply_theme(self, theme: Component):
+        """Apply a theme to the component and its children.
+
+        Args:
+            theme: The theme to apply.
+        """
+        self._apply_theme(theme)
+        for child in self.children:
+            if not isinstance(child, Component):
+                continue
+            child.apply_theme(theme)
+
     def _render(self, props: dict[str, Any] | None = None) -> Tag:
     def _render(self, props: dict[str, Any] | None = None) -> Tag:
         """Define how to render the component in React.
         """Define how to render the component in React.
 
 
@@ -603,7 +624,7 @@ class Component(BaseComponent, ABC):
         Returns:
         Returns:
             The dictionary of the component style as value and the style notation as key.
             The dictionary of the component style as value and the style notation as key.
         """
         """
-        return {"style": self.style}
+        return {"css": self.style}
 
 
     def render(self) -> Dict:
     def render(self) -> Dict:
         """Render the component.
         """Render the component.

+ 3 - 0
reflex/components/radix/primitives/__init__.py

@@ -0,0 +1,3 @@
+"""Radix primitive components (https://www.radix-ui.com/primitives)."""
+
+from .accordion import accordion

+ 279 - 0
reflex/components/radix/primitives/accordion.py

@@ -0,0 +1,279 @@
+"""Radix accordion components."""
+
+from typing import Literal
+
+from reflex.components.component import Component
+from reflex.components.tags import Tag
+from reflex.style import Style
+from reflex.utils import format, imports
+from reflex.vars import Var
+
+LiteralAccordionType = Literal["single", "multiple"]
+LiteralAccordionDir = Literal["ltr", "rtl"]
+LiteralAccordionOrientation = Literal["vertical", "horizontal"]
+
+
+DEFAULT_ANIMATION_DURATION = 250
+
+
+class AccordionComponent(Component):
+    """Base class for all @radix-ui/accordion components."""
+
+    library = "@radix-ui/react-accordion@^1.1.2"
+
+    # Change the default rendered element for the one passed as a child.
+    as_child: Var[bool]
+
+    def _render(self) -> Tag:
+        return (
+            super()
+            ._render()
+            .add_props(
+                **{
+                    "class_name": format.to_title_case(self.tag or ""),
+                }
+            )
+        )
+
+
+class AccordionRoot(AccordionComponent):
+    """An accordion component."""
+
+    tag = "Root"
+
+    alias = "RadixAccordionRoot"
+
+    # The type of accordion (single or multiple).
+    type_: Var[LiteralAccordionType]
+
+    # The value of the item to expand.
+    value: Var[str]
+
+    # The default value of the item to expand.
+    default_value: Var[str]
+
+    # Whether or not the accordion is collapsible.
+    collapsible: Var[bool]
+
+    # Whether or not the accordion is disabled.
+    disabled: Var[bool]
+
+    # The reading direction of the accordion when applicable.
+    dir: Var[LiteralAccordionDir]
+
+    # The orientation of the accordion.
+    orientation: Var[LiteralAccordionOrientation]
+
+    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,
+            }
+        )
+
+
+class AccordionItem(AccordionComponent):
+    """An accordion component."""
+
+    tag = "Item"
+
+    alias = "RadixAccordionItem"
+
+    # A unique identifier for the item.
+    value: Var[str]
+
+    # When true, prevents the user from interacting with the item.
+    disabled: Var[bool]
+
+    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,
+            }
+        )
+
+
+class AccordionHeader(AccordionComponent):
+    """An accordion component."""
+
+    tag = "Header"
+
+    alias = "RadixAccordionHeader"
+
+    def _apply_theme(self, theme: Component):
+        self.style = Style(
+            {
+                "display": "flex",
+                **self.style,
+            }
+        )
+
+
+class AccordionTrigger(AccordionComponent):
+    """An accordion component."""
+
+    tag = "Trigger"
+
+    alias = "RadixAccordionTrigger"
+
+    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)",
+                },
+                "&[data-state='open'] > .AccordionChevron": {
+                    "transform": "rotate(180deg)",
+                },
+                **self.style,
+            }
+        )
+
+
+class AccordionContent(AccordionComponent):
+    """An accordion component."""
+
+    tag = "Content"
+
+    alias = "RadixAccordionContent"
+
+    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,
+            }
+        )
+
+    def _get_imports(self):
+        return {
+            **super()._get_imports(),
+            "@emotion/react": [imports.ImportVar(tag="keyframes")],
+        }
+
+    def _get_custom_code(self) -> str:
+        return """
+const slideDown = keyframes`
+from {
+  height: 0;
+}
+to {
+  height: var(--radix-accordion-content-height);
+}
+`
+const slideUp = keyframes`
+from {
+  height: var(--radix-accordion-content-height);
+}
+to {
+  height: 0;
+}
+`
+"""
+
+
+# TODO: Remove this once the radix-icons PR is merged in.
+class ChevronDownIcon(Component):
+    """A chevron down icon."""
+
+    library = "@radix-ui/react-icons"
+
+    tag = "ChevronDownIcon"
+
+    def _apply_theme(self, theme: Component):
+        self.style = Style(
+            {
+                "color": "var(--accent-10)",
+                "transition": f"transform {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
+                **self.style,
+            }
+        )
+
+
+accordion_root = AccordionRoot.create
+accordion_item = AccordionItem.create
+accordion_trigger = AccordionTrigger.create
+accordion_content = AccordionContent.create
+accordion_header = AccordionHeader.create
+chevron_down_icon = ChevronDownIcon.create
+
+
+def accordion(items: list[tuple[str, str]], **props) -> Component:
+    """High level API for the Radix accordion.
+
+    #TODO: We need to handle taking in state here. This is just for a POC.
+
+
+    Args:
+        items: The items of the accordion component: list of tuples (label,panel)
+        **props: The properties of the component.
+
+    Returns:
+        The accordion component.
+    """
+    return accordion_root(
+        *[
+            accordion_item(
+                accordion_header(
+                    accordion_trigger(
+                        label,
+                        chevron_down_icon(
+                            class_name="AccordionChevron",
+                        ),
+                    ),
+                ),
+                accordion_content(
+                    panel,
+                ),
+                value=f"item-{i}",
+            )
+            for i, (label, panel) in enumerate(items)
+        ],
+        **props,
+    )

+ 619 - 0
reflex/components/radix/primitives/accordion.pyi

@@ -0,0 +1,619 @@
+"""Stub file for reflex/components/radix/primitives/accordion.py"""
+# ------------------- DO NOT EDIT ----------------------
+# This file was generated by `scripts/pyi_generator.py`!
+# ------------------------------------------------------
+
+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 reflex.components.component import Component
+from reflex.components.tags import Tag
+from reflex.style import Style
+from reflex.utils import format, imports
+from reflex.vars import Var
+
+LiteralAccordionType = Literal["single", "multiple"]
+LiteralAccordionDir = Literal["ltr", "rtl"]
+LiteralAccordionOrientation = Literal["vertical", "horizontal"]
+DEFAULT_ANIMATION_DURATION = 250
+
+class AccordionComponent(Component):
+    @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
+    ) -> "AccordionComponent":
+        """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.
+        """
+        ...
+
+class AccordionRoot(AccordionComponent):
+    @overload
+    @classmethod
+    def create(  # type: ignore
+        cls,
+        *children,
+        type_: Optional[
+            Union[Var[Literal["single", "multiple"]], Literal["single", "multiple"]]
+        ] = None,
+        value: Optional[Union[Var[str], str]] = None,
+        default_value: Optional[Union[Var[str], str]] = None,
+        collapsible: Optional[Union[Var[bool], bool]] = None,
+        disabled: Optional[Union[Var[bool], bool]] = None,
+        dir: Optional[Union[Var[Literal["ltr", "rtl"]], Literal["ltr", "rtl"]]] = None,
+        orientation: Optional[
+            Union[
+                Var[Literal["vertical", "horizontal"]],
+                Literal["vertical", "horizontal"],
+            ]
+        ] = 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
+    ) -> "AccordionRoot":
+        """Create the component.
+
+        Args:
+            *children: The children of the component.
+            type_: The type of accordion (single or multiple).
+            value: The value of the item to expand.
+            default_value: The default value of the item to expand.
+            collapsible: Whether or not the accordion is collapsible.
+            disabled: Whether or not the accordion is disabled.
+            dir: The reading direction of the accordion when applicable.
+            orientation: The orientation of the accordion.
+            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.
+        """
+        ...
+
+class AccordionItem(AccordionComponent):
+    @overload
+    @classmethod
+    def create(  # type: ignore
+        cls,
+        *children,
+        value: Optional[Union[Var[str], str]] = None,
+        disabled: Optional[Union[Var[bool], bool]] = 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
+    ) -> "AccordionItem":
+        """Create the component.
+
+        Args:
+            *children: The children of the component.
+            value: A unique identifier for the item.
+            disabled: When true, prevents the user from interacting with the item.
+            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.
+        """
+        ...
+
+class AccordionHeader(AccordionComponent):
+    @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
+    ) -> "AccordionHeader":
+        """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.
+        """
+        ...
+
+class AccordionTrigger(AccordionComponent):
+    @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
+    ) -> "AccordionTrigger":
+        """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.
+        """
+        ...
+
+class AccordionContent(AccordionComponent):
+    @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
+    ) -> "AccordionContent":
+        """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.
+        """
+        ...
+
+class ChevronDownIcon(Component):
+    @overload
+    @classmethod
+    def create(  # type: ignore
+        cls,
+        *children,
+        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
+    ) -> "ChevronDownIcon":
+        """Create the component.
+
+        Args:
+            *children: The children of the component.
+            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.
+        """
+        ...
+
+accordion_root = AccordionRoot.create
+accordion_item = AccordionItem.create
+accordion_trigger = AccordionTrigger.create
+accordion_content = AccordionContent.create
+accordion_header = AccordionHeader.create
+chevron_down_icon = ChevronDownIcon.create
+
+def accordion(items: list[tuple[str, str]], **props) -> Component: ...

+ 141 - 0
reflex/components/radix/themes/typography.py

@@ -0,0 +1,141 @@
+"""Components for rendering text.
+
+https://www.radix-ui.com/themes/docs/theme/typography
+"""
+from __future__ import annotations
+
+from typing import Literal
+
+from reflex.vars import Var
+
+from .base import (
+    CommonMarginProps,
+    LiteralAccentColor,
+    LiteralVariant,
+    RadixThemesComponent,
+)
+
+LiteralTextWeight = Literal["light", "regular", "medium", "bold"]
+LiteralTextAlign = Literal["left", "center", "right"]
+LiteralTextSize = Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"]
+LiteralTextTrim = Literal["normal", "start", "end", "both"]
+
+
+class Text(CommonMarginProps, RadixThemesComponent):
+    """A foundational text primitive based on the <span> element."""
+
+    tag = "Text"
+
+    # Change the default rendered element for the one passed as a child, merging their props and behavior.
+    as_child: Var[bool]
+
+    # Change the default rendered element into a semantically appropriate alternative (cannot be used with asChild)
+    as_: Var[str]
+
+    # Text size: "1" - "9"
+    size: Var[LiteralTextSize]
+
+    # Thickness of text: "light" | "regular" | "medium" | "bold"
+    weight: Var[LiteralTextWeight]
+
+    # Alignment of text in element: "left" | "center" | "right"
+    align: Var[LiteralTextAlign]
+
+    # Removes the leading trim space: "normal" | "start" | "end" | "both"
+    trim: Var[LiteralTextTrim]
+
+    # Overrides the accent color inherited from the Theme.
+    color: Var[LiteralAccentColor]
+
+    # Whether to render the text with higher contrast color
+    high_contrast: Var[bool]
+
+
+class Heading(Text):
+    """A semantic heading element."""
+
+    tag = "Heading"
+
+
+class Blockquote(CommonMarginProps, RadixThemesComponent):
+    """A block level extended quotation."""
+
+    tag = "Blockquote"
+
+    # Text size: "1" - "9"
+    size: Var[LiteralTextSize]
+
+    # Thickness of text: "light" | "regular" | "medium" | "bold"
+    weight: Var[LiteralTextWeight]
+
+    # Overrides the accent color inherited from the Theme.
+    color: Var[LiteralAccentColor]
+
+    # Whether to render the text with higher contrast color
+    high_contrast: Var[bool]
+
+
+class Code(Blockquote):
+    """Marks text to signify a short fragment of computer code."""
+
+    tag = "Code"
+
+    # The visual variant to apply: "solid" | "soft" | "outline" | "ghost"
+    variant: Var[LiteralVariant]
+
+
+class Em(CommonMarginProps, RadixThemesComponent):
+    """Marks text to stress emphasis."""
+
+    tag = "Em"
+
+
+class Kbd(CommonMarginProps, RadixThemesComponent):
+    """Represents keyboard input or a hotkey."""
+
+    tag = "Kbd"
+
+    # Text size: "1" - "9"
+    size: Var[LiteralTextSize]
+
+
+LiteralLinkUnderline = Literal["auto", "hover", "always"]
+
+
+class Link(CommonMarginProps, RadixThemesComponent):
+    """A semantic element for navigation between pages."""
+
+    tag = "Link"
+
+    # Change the default rendered element for the one passed as a child, merging their props and behavior.
+    as_child: Var[bool]
+
+    # Text size: "1" - "9"
+    size: Var[LiteralTextSize]
+
+    # Thickness of text: "light" | "regular" | "medium" | "bold"
+    weight: Var[LiteralTextWeight]
+
+    # Removes the leading trim space: "normal" | "start" | "end" | "both"
+    trim: Var[LiteralTextTrim]
+
+    # Sets the visibility of the underline affordance: "auto" | "hover" | "always"
+    underline: Var[LiteralLinkUnderline]
+
+    # Overrides the accent color inherited from the Theme.
+    color: Var[LiteralAccentColor]
+
+    # Whether to render the text with higher contrast color
+    high_contrast: Var[bool]
+
+
+class Quote(CommonMarginProps, RadixThemesComponent):
+    """A short inline quotation."""
+
+    tag = "Quote"
+
+
+class Strong(CommonMarginProps, RadixThemesComponent):
+    """Marks text to signify strong importance."""
+
+    tag = "Strong"

+ 23 - 0
reflex/vars.py

@@ -1284,6 +1284,29 @@ class Var:
             _var_type=str,
             _var_type=str,
         )
         )
 
 
+    def strip(self, other: str | Var[str] = " ") -> Var:
+        """Strip a string var.
+
+        Args:
+            other: The string to strip the var with.
+
+        Returns:
+            A var with the stripped string.
+
+        Raises:
+            TypeError: If the var is not a string.
+        """
+        if not types._issubclass(self._var_type, str):
+            raise TypeError(f"Cannot strip non-string var {self._var_full_name}.")
+
+        other = Var.create_safe(json.dumps(other)) if isinstance(other, str) else other
+
+        return self._replace(
+            _var_name=f"{self._var_name}.replace(/^${other._var_full_name}|${other._var_full_name}$/g, '')",
+            _var_is_string=False,
+            merge_var_data=other._var_data,
+        )
+
     def split(self, other: str | Var[str] = " ") -> Var:
     def split(self, other: str | Var[str] = " ") -> Var:
         """Split a string var into a list.
         """Split a string var into a list.