Added Layout and UserProfile

This commit is contained in:
2025-11-10 21:19:38 +01:00
parent a547b2b882
commit d302261d07
12 changed files with 249 additions and 87 deletions

View File

@@ -5,15 +5,15 @@ 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.controls.UserProfile import UserProfile
from myfasthtml.controls.helpers import mk, Ids
from myfasthtml.core.commands import Command
from myfasthtml.core.instances import MultipleInstance, InstancesManager
from myfasthtml.icons.fluent import panel_left_expand20_regular as left_drawer_icon
from myfasthtml.icons.fluent_p2 import panel_right_expand20_regular as right_drawer_icon
@@ -23,6 +23,7 @@ logger = logging.getLogger("LayoutControl")
@dataclass
class LayoutState:
left_drawer_open: bool = True
right_drawer_open: bool = False
class Commands(BaseCommands):
@@ -30,7 +31,7 @@ class Commands(BaseCommands):
return Command("ToggleDrawer", "Toggle main layout drawer", self._owner.toggle_drawer, "left")
class Layout(BaseControl):
class Layout(MultipleInstance):
"""
A responsive layout component with header, footer, main content area,
and optional collapsible side drawers.
@@ -41,7 +42,19 @@ class Layout(BaseControl):
right_drawer (bool): Whether to include a right drawer
"""
def __init__(self, session, app_name, left_drawer=True, right_drawer=True):
class DrawerContent:
def __init__(self, owner, side: Literal["left", "right"]):
self._owner = owner
self.side = side
self._content = []
def append(self, content):
self._content.append(content)
def get_content(self):
return self._content
def __init__(self, session, app_name):
"""
Initialize the Layout component.
@@ -50,28 +63,17 @@ class Layout(BaseControl):
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())}")
super().__init__(session, Ids.Layout)
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
self.left_drawer = self.DrawerContent(self, "left")
self.right_drawer = self.DrawerContent(self, "right")
def set_footer(self, content):
"""
@@ -91,26 +93,6 @@ class Layout(BaseControl):
"""
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":
@@ -118,7 +100,7 @@ class Layout(BaseControl):
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()
return self._mk_left_drawer_icon(), self._mk_right_drawer()
else:
raise ValueError("Invalid drawer side")
@@ -131,6 +113,7 @@ class Layout(BaseControl):
"""
return Header(
self._mk_left_drawer_icon(),
InstancesManager.get(self._session, Ids.UserProfile, UserProfile),
cls="mf-layout-header"
)
@@ -167,17 +150,10 @@ class Layout(BaseControl):
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,
*self.left_drawer.get_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):
@@ -187,15 +163,10 @@ class Layout(BaseControl):
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",
*self.right_drawer.get_content(),
cls=f"mf-layout-drawer mf-layout-right-drawer {'collapsed' if not self._state.right_drawer_open else ''}",
id=f"{self._id}_rd",
**{"data-side": "right"}
)
def _mk_left_drawer_icon(self):
@@ -218,13 +189,8 @@ class Layout(BaseControl):
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):