Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions reflex_ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"components.base.button": ["button"],
"components.base.card": ["card"],
"components.base.checkbox": ["checkbox"],
"components.base.collapsible": ["collapsible"],
"components.base.dialog": ["dialog"],
"components.base.gradient_profile": ["gradient_profile"],
"components.base.input": ["input"],
Expand Down
3 changes: 3 additions & 0 deletions reflex_ui/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ from .components.base.badge import badge
from .components.base.button import button
from .components.base.card import card
from .components.base.checkbox import checkbox
from .components.base.collapsible import collapsible
from .components.base.dialog import dialog
from .components.base.gradient_profile import gradient_profile
from .components.base.input import input
Expand Down Expand Up @@ -39,6 +40,7 @@ _REFLEX_UI_MAPPING = {
"components.base.button": ["button"],
"components.base.card": ["card"],
"components.base.checkbox": ["checkbox"],
"components.base.collapsible": ["collapsible"],
"components.base.dialog": ["dialog"],
"components.base.gradient_profile": ["gradient_profile"],
"components.base.input": ["input"],
Expand Down Expand Up @@ -76,6 +78,7 @@ __all__ = [
"card",
"checkbox",
"cn",
"collapsible",
"components",
"dialog",
"gradient_profile",
Expand Down
2 changes: 2 additions & 0 deletions reflex_ui/components/base/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ from .badge import badge
from .button import button
from .card import card
from .checkbox import checkbox
from .collapsible import collapsible
from .dialog import dialog
from .gradient_profile import gradient_profile
from .input import input
Expand Down Expand Up @@ -41,6 +42,7 @@ __all__ = [
"button",
"card",
"checkbox",
"collapsible",
"dialog",
"gradient_profile",
"input",
Expand Down
140 changes: 140 additions & 0 deletions reflex_ui/components/base/collapsible.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
"""Custom collapsible component."""

from reflex.components.component import Component, ComponentNamespace
from reflex.event import EventHandler, passthrough_event_spec
from reflex.utils.imports import ImportVar
from reflex.vars.base import Var

from reflex_ui.components.base_ui import PACKAGE_NAME, BaseUIComponent


class ClassNames:
"""Class names for collapsible components."""

ROOT = "flex flex-col justify-center text-secondary-12"
TRIGGER = "group flex items-center gap-2"
PANEL = "flex h-[var(--collapsible-panel-height)] flex-col justify-end overflow-hidden text-sm transition-all ease-out data-[ending-style]:h-0 data-[starting-style]:h-0"


class CollapsibleBaseComponent(BaseUIComponent):
"""Base component for collapsible components."""

library = f"{PACKAGE_NAME}/collapsible"

@property
def import_var(self):
"""Return the import variable for the collapsible component."""
return ImportVar(tag="Collapsible", package_path="", install=False)


class CollapsibleRoot(CollapsibleBaseComponent):
"""Groups all parts of the collapsible. Renders a <div> element."""

tag = "Collapsible.Root"

# Whether the collapsible panel is initially open. To render a controlled collapsible, use the `open` prop instead. Defaults to False.
default_open: Var[bool]

# Whether the collapsible panel is currently open. To render an uncontrolled collapsible, use the `default_open` prop instead.
open: Var[bool]

# Event handler called when the panel is opened or closed.
on_open_change: EventHandler[passthrough_event_spec(bool)]

# Whether the component should ignore user interaction. Defaults to False.
disabled: Var[bool]

# The render prop.
render_: Var[Component]

@classmethod
def create(cls, *children, **props) -> BaseUIComponent:
"""Create the collapsible root component."""
props["data-slot"] = "collapsible"
cls.set_class_name(ClassNames.ROOT, props)
return super().create(*children, **props)


class CollapsibleTrigger(CollapsibleBaseComponent):
"""A button that opens and closes the collapsible panel. Renders a <button> element."""

tag = "Collapsible.Trigger"

# Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (e.g. `<div>`). Defaults to True.
native_button: Var[bool]

# The render prop.
render_: Var[Component]

@classmethod
def create(cls, *children, **props) -> BaseUIComponent:
"""Create the collapsible trigger component."""
props["data-slot"] = "collapsible-trigger"
cls.set_class_name(ClassNames.TRIGGER, props)
return super().create(*children, **props)


class CollapsiblePanel(CollapsibleBaseComponent):
"""A panel with the collapsible contents. Renders a <div> element."""

tag = "Collapsible.Panel"

# Allows the browser's built-in page search to find and expand the panel contents. Overrides the `keep_mounted` prop and uses `hidden="until-found"` to hide the element without removing it from the DOM. Defaults to False.
hidden_until_found: Var[bool]

# Whether to keep the element in the DOM while the panel is hidden. This prop is ignored when `hidden_until_found` is used. Defaults to False.
keep_mounted: Var[bool]

# Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.
render_: Var[Component]

@classmethod
def create(cls, *children, **props) -> BaseUIComponent:
"""Create the collapsible panel component."""
props["data-slot"] = "collapsible-panel"
cls.set_class_name(ClassNames.PANEL, props)
return super().create(*children, **props)


class HighLevelCollapsible(CollapsibleRoot):
"""High level collapsible component."""

# The trigger component.
trigger: Var[Component | None]

# The content component.
content: Var[str | Component | None]

@classmethod
def create(cls, *children, **props) -> BaseUIComponent:
"""Create the collapsible component."""
trigger = props.pop("trigger", None)
content = props.pop("content", None)

return CollapsibleRoot.create(
CollapsibleTrigger.create(render_=trigger) if trigger else None,
CollapsiblePanel.create(
content,
*children,
),
**props,
)

def _exclude_props(self) -> list[str]:
return [
*super()._exclude_props(),
"trigger",
"content",
]


class Collapsible(ComponentNamespace):
"""Namespace for Collapsible components."""

root = staticmethod(CollapsibleRoot.create)
trigger = staticmethod(CollapsibleTrigger.create)
panel = staticmethod(CollapsiblePanel.create)
__call__ = staticmethod(HighLevelCollapsible.create)


collapsible = Collapsible()
Loading