I can toogle the left drawer

This commit is contained in:
2025-11-10 08:44:59 +01:00
parent 459c89bae2
commit 5cb628099a
9 changed files with 588 additions and 4 deletions

View File

@@ -0,0 +1,236 @@
"""
Layout component for FastHTML applications.
This component provides a responsive layout with fixed header/footer,
optional collapsible left/right drawers, and a scrollable main content area.
"""
import logging
import uuid
from typing import Literal
from fasthtml.common import *
from myfasthtml.controls.BaseCommands import BaseCommands
from myfasthtml.controls.BaseControl import BaseControl
from myfasthtml.controls.helpers import mk
from myfasthtml.core.commands import Command
from myfasthtml.icons.fluent import icon_panel_left_expand20_regular as left_drawer_icon
from myfasthtml.icons.fluent import icon_panel_right_expand20_regular as right_drawer_icon
logger = logging.getLogger("LayoutControl")
@dataclass
class LayoutState:
left_drawer_open: bool = True
class Commands(BaseCommands):
def toggle_left_drawer(self):
return Command("ToggleDrawer", "Toggle main layout drawer", self._owner.toggle_drawer, "left")
class Layout(BaseControl):
"""
A responsive layout component with header, footer, main content area,
and optional collapsible side drawers.
Attributes:
app_name (str): Name of the application
left_drawer (bool): Whether to include a left drawer
right_drawer (bool): Whether to include a right drawer
"""
def __init__(self, session, app_name, left_drawer=True, right_drawer=True):
"""
Initialize the Layout component.
Args:
app_name (str): Name of the application
left_drawer (bool): Enable left drawer. Default is True.
right_drawer (bool): Enable right drawer. Default is True.
"""
super().__init__(session, f"mf-layout-{str(uuid.uuid4())}")
self.app_name = app_name
self.left_drawer = left_drawer
self.right_drawer = right_drawer
# Content storage
self._header_content = None
self._footer_content = None
self._main_content = None
self._left_drawer_content = None
self._right_drawer_content = None
self._state = LayoutState()
self.commands = Commands(self)
# def set_header(self, content):
# """
# Set the header content.
#
# Args:
# content: FastHTML component(s) or content for the header
# """
# self._header_content = content
def set_footer(self, content):
"""
Set the footer content.
Args:
content: FastHTML component(s) or content for the footer
"""
self._footer_content = content
def set_main(self, content):
"""
Set the main content area.
Args:
content: FastHTML component(s) or content for the main area
"""
self._main_content = content
def set_left_drawer(self, content):
"""
Set the left drawer content.
Args:
content: FastHTML component(s) or content for the left drawer
"""
if self.left_drawer:
self._left_drawer_content = content
def set_right_drawer(self, content):
"""
Set the right drawer content.
Args:
content: FastHTML component(s) or content for the right drawer
"""
if self.right_drawer:
self._right_drawer_content = content
def toggle_drawer(self, side: Literal["left", "right"]):
logger.debug(f"Toggle drawer: {side=}, {self._state.left_drawer_open=}")
if side == "left":
self._state.left_drawer_open = not self._state.left_drawer_open
return self._mk_left_drawer_icon(), self._mk_left_drawer()
elif side == "right":
self._state.right_drawer_open = not self._state.right_drawer_open
return Div(), self._mk_right_drawer()
else:
raise ValueError("Invalid drawer side")
def _mk_header(self):
"""
Render the header component.
Returns:
Header: FastHTML Header component
"""
return Header(
self._mk_left_drawer_icon(),
cls="mf-layout-header"
)
def _mk_footer(self):
"""
Render the footer component.
Returns:
Footer: FastHTML Footer component
"""
footer_content = self._footer_content if self._footer_content else ""
return Footer(
footer_content,
cls="mf-layout-footer footer sm:footer-horizontal"
)
def _mk_main(self):
"""
Render the main content area.
Returns:
Main: FastHTML Main component
"""
main_content = self._main_content if self._main_content else ""
return Main(
main_content,
cls="mf-layout-main"
)
def _mk_left_drawer(self):
"""
Render the left drawer if enabled.
Returns:
Div or None: FastHTML Div component for left drawer, or None if disabled
"""
if not self.left_drawer:
return None
print(f"{self._state.left_drawer_open=}")
drawer_content = self._left_drawer_content if self._left_drawer_content else ""
return Div(
drawer_content,
id=f"{self._id}_ld",
cls=f"mf-layout-drawer mf-layout-left-drawer {'collapsed' if not self._state.left_drawer_open else ''}",
**{"data-side": "left"}
)
def _mk_right_drawer(self):
"""
Render the right drawer if enabled.
Returns:
Div or None: FastHTML Div component for right drawer, or None if disabled
"""
if not self.right_drawer:
return None
drawer_content = self._right_drawer_content if self._right_drawer_content else ""
return Div(
drawer_content,
cls="mf-layout-drawer mf-layout-right-drawer",
id=f"{self._id}_rd",
**{"data-side": "right"}
)
def _mk_left_drawer_icon(self):
return mk.icon(right_drawer_icon if self._state.left_drawer_open else left_drawer_icon,
id=f"{self._id}_ldi",
command=self.commands.toggle_left_drawer())
def render(self):
"""
Render the complete layout.
Returns:
Div: Complete layout as FastHTML Div component
"""
# Wrap everything in a container div
return Div(
self._mk_header(),
self._mk_left_drawer(),
self._mk_main(),
self._mk_right_drawer(),
self._mk_footer(),
Script(f"initLayout('{self._id}');"),
id=self._id,
cls="mf-layout",
**{
"data-left-drawer": str(self.left_drawer).lower(),
"data-right-drawer": str(self.right_drawer).lower()
}
)
def __ft__(self):
"""
FastHTML magic method for rendering.
Returns:
Div: The rendered layout
"""
return self.render()