123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- """To experiment with layout component, move them to reflex/components later."""
- from __future__ import annotations
- from typing import Any, List
- from reflex import color, cond
- from reflex.components.base.fragment import Fragment
- from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf
- from reflex.components.radix.primitives.drawer import DrawerRoot, drawer
- from reflex.components.radix.themes.components.icon_button import IconButton
- from reflex.components.radix.themes.layout.box import Box
- from reflex.components.radix.themes.layout.container import Container
- from reflex.components.radix.themes.layout.stack import HStack
- from reflex.event import run_script
- from reflex.experimental import hooks
- from reflex.state import ComponentState
- from reflex.style import Style
- from reflex.vars.base import Var
- class Sidebar(Box, MemoizationLeaf):
- """A component that renders the sidebar."""
- @classmethod
- def create(cls, *children, **props):
- """Create the sidebar component.
- Args:
- children: The children components.
- props: The properties of the sidebar.
- Returns:
- The sidebar component.
- """
- # props.setdefault("border_right", f"1px solid {color('accent', 12)}")
- # props.setdefault("background_color", color("accent", 1))
- # props.setdefault("width", "20vw")
- # props.setdefault("height", "100vh")
- # props.setdefault("position", "fixed")
- return super().create(
- Box.create(*children, **props), # sidebar for content
- Box.create(width=props.get("width")), # spacer for layout
- )
- def add_style(self) -> dict[str, Any] | None:
- """Add style to the component.
- Returns:
- The style of the component.
- """
- sidebar: Component = self.children[-2] # type: ignore
- spacer: Component = self.children[-1] # type: ignore
- open = (
- self.State.open # type: ignore
- if self.State
- else Var(_js_expr="open")
- )
- sidebar.style["display"] = spacer.style["display"] = cond(open, "block", "none")
- return Style(
- {
- "position": "fixed",
- "border_right": f"1px solid {color('accent', 12)}",
- "background_color": color("accent", 1),
- "width": "20vw",
- "height": "100vh",
- }
- )
- def add_hooks(self) -> List[Var]:
- """Get the hooks to render.
- Returns:
- The hooks for the sidebar.
- """
- return [hooks.useState("open", "true")] if not self.State else []
- class StatefulSidebar(ComponentState):
- """Bind a state to a sidebar component."""
- open: bool = True
- def toggle(self):
- """Toggle the sidebar."""
- self.open = not self.open
- @classmethod
- def get_component(cls, *children, **props):
- """Get the stateful sidebar component.
- Args:
- children: The children components.
- props: The properties of the sidebar.
- Returns:
- The stateful sidebar component.
- """
- return Sidebar.create(*children, **props)
- class DrawerSidebar(DrawerRoot):
- """A component that renders a drawer sidebar."""
- @classmethod
- def create(cls, *children, **props):
- """Create the sidebar component.
- Args:
- children: The children components.
- props: The properties of the sidebar.
- Returns:
- The drawer sidebar component.
- """
- direction = props.pop("direction", "left")
- props.setdefault("border_right", f"1px solid {color('accent', 12)}")
- props.setdefault("background_color", color("accent", 1))
- props.setdefault("width", "20vw")
- props.setdefault("height", "100vh")
- return super().create(
- drawer.trigger(
- IconButton.create(
- "arrow-right-from-line",
- background_color="transparent",
- ),
- position="absolute",
- top="15",
- left="15",
- ),
- drawer.portal(
- drawer.content(
- *children,
- **props,
- )
- ),
- direction=direction,
- )
- sidebar_trigger_style = {
- "position": "fixed",
- "z_index": "15",
- "color": color("accent", 12),
- "background_color": "transparent",
- "padding": "0",
- }
- class SidebarTrigger(Fragment):
- """A component that renders the sidebar trigger."""
- @classmethod
- def create(cls, sidebar: Component, **props):
- """Create the sidebar trigger component.
- Args:
- sidebar: The sidebar component.
- props: The properties of the sidebar trigger.
- Returns:
- The sidebar trigger component.
- """
- trigger_props = {**props, **sidebar_trigger_style}
- inner_sidebar: Component = sidebar.children[0] # type: ignore
- sidebar_width = inner_sidebar.style.get("width")
- if sidebar.State:
- open, toggle = sidebar.State.open, sidebar.State.toggle # type: ignore
- else:
- open, toggle = (
- Var(_js_expr="open"),
- run_script("setOpen(!open)"),
- )
- trigger_props["left"] = cond(open, f"calc({sidebar_width} - 32px)", "0")
- trigger = cond(
- open,
- IconButton.create(
- "arrow-left-from-line",
- on_click=toggle,
- **trigger_props,
- ),
- IconButton.create(
- "arrow-right-from-line",
- on_click=toggle,
- **trigger_props,
- ),
- )
- return super().create(trigger)
- class Layout(Box):
- """A component that renders the layout."""
- @classmethod
- def create(
- cls,
- *content: Component,
- sidebar: Component | None = None,
- **props,
- ):
- """Create the layout component.
- Args:
- content: The content component.
- sidebar: The sidebar component.
- props: The properties of the layout.
- Returns:
- The layout component.
- """
- layout_root = HStack.create
- if sidebar is None:
- return Container.create(*content, **props)
- if isinstance(sidebar, DrawerSidebar):
- return super().create(
- sidebar,
- Container.create(*content, height="100%"),
- **props,
- width="100vw",
- min_height="100vh",
- )
- if not isinstance(sidebar, Sidebar):
- sidebar = Sidebar.create(sidebar)
- # Add the sidebar trigger to the sidebar as first child to not mess up the rendering.
- sidebar.children.insert(0, SidebarTrigger.create(sidebar))
- return super().create(
- layout_root(
- sidebar,
- Container.create(*content, height="100%"),
- **props,
- width="100vw",
- min_height="100vh",
- )
- )
- class LayoutNamespace(ComponentNamespace):
- """Namespace for layout components."""
- drawer_sidebar = staticmethod(DrawerSidebar.create)
- stateful_sidebar = staticmethod(StatefulSidebar.create)
- sidebar = staticmethod(Sidebar.create)
- __call__ = staticmethod(Layout.create)
- layout = LayoutNamespace()
|