Added Controls testing + documentation
This commit is contained in:
@@ -17,15 +17,17 @@ from myfasthtml.core.commands import Command
|
||||
from myfasthtml.core.dbmanager import DbObject
|
||||
from myfasthtml.core.instances import SingleInstance
|
||||
from myfasthtml.core.utils import get_id
|
||||
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
|
||||
from myfasthtml.icons.fluent import panel_left_contract20_regular as left_drawer_contract
|
||||
from myfasthtml.icons.fluent import panel_left_expand20_regular as left_drawer_expand
|
||||
from myfasthtml.icons.fluent_p1 import panel_right_contract20_regular as right_drawer_contract
|
||||
from myfasthtml.icons.fluent_p2 import panel_right_expand20_regular as right_drawer_expand
|
||||
|
||||
logger = logging.getLogger("LayoutControl")
|
||||
|
||||
|
||||
class LayoutState(DbObject):
|
||||
def __init__(self, owner):
|
||||
super().__init__(owner)
|
||||
def __init__(self, owner, name=None):
|
||||
super().__init__(owner, name=name)
|
||||
with self.initializing():
|
||||
self.left_drawer_open: bool = True
|
||||
self.right_drawer_open: bool = True
|
||||
@@ -37,12 +39,13 @@ class Commands(BaseCommands):
|
||||
def toggle_drawer(self, side: Literal["left", "right"]):
|
||||
return Command("ToggleDrawer", f"Toggle {side} layout drawer", self._owner.toggle_drawer, side)
|
||||
|
||||
def update_drawer_width(self, side: Literal["left", "right"]):
|
||||
def update_drawer_width(self, side: Literal["left", "right"], width: int = None):
|
||||
"""
|
||||
Create a command to update drawer width.
|
||||
|
||||
Args:
|
||||
side: Which drawer to update ("left" or "right")
|
||||
width: New width in pixels. Given by the HTMX request
|
||||
|
||||
Returns:
|
||||
Command: Command object for updating drawer width
|
||||
@@ -114,7 +117,7 @@ class Layout(SingleInstance):
|
||||
|
||||
# Content storage
|
||||
self._main_content = None
|
||||
self._state = LayoutState(self)
|
||||
self._state = LayoutState(self, "default_layout")
|
||||
self._boundaries = Boundaries(self)
|
||||
self.commands = Commands(self)
|
||||
self.left_drawer = self.Content(self)
|
||||
@@ -123,16 +126,6 @@ class Layout(SingleInstance):
|
||||
self.header_right = self.Content(self)
|
||||
self.footer_left = self.Content(self)
|
||||
self.footer_right = self.Content(self)
|
||||
self._footer_content = None
|
||||
|
||||
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):
|
||||
"""
|
||||
@@ -145,6 +138,18 @@ class Layout(SingleInstance):
|
||||
return self
|
||||
|
||||
def toggle_drawer(self, side: Literal["left", "right"]):
|
||||
"""
|
||||
Toggle the state of a drawer (open or close) based on the specified side. This
|
||||
method also generates the corresponding icon and drawer elements for the
|
||||
selected side.
|
||||
|
||||
:param side: The side of the drawer to toggle. Must be either "left" or "right".
|
||||
:type side: Literal["left", "right"]
|
||||
:return: A tuple containing the updated drawer icon and drawer elements for
|
||||
the specified side.
|
||||
:rtype: Tuple[Any, Any]
|
||||
:raises ValueError: If the provided `side` is not "left" or "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
|
||||
@@ -190,15 +195,17 @@ class Layout(SingleInstance):
|
||||
return Header(
|
||||
Div( # left
|
||||
self._mk_left_drawer_icon(),
|
||||
*self.header_left.get_content(),
|
||||
cls="flex gap-1"
|
||||
*self._mk_content_wrapper(self.header_left, horizontal=True, show_group_name=False).children,
|
||||
cls="flex gap-1",
|
||||
id=f"{self._id}_hl"
|
||||
),
|
||||
Div( # right
|
||||
*self.header_right.get_content()[None],
|
||||
*self._mk_content_wrapper(self.header_right, horizontal=True, show_group_name=False).children,
|
||||
UserProfile(self),
|
||||
cls="flex gap-1"
|
||||
cls="flex gap-1",
|
||||
id=f"{self._id}_hr"
|
||||
),
|
||||
cls="mf-layout-header"
|
||||
cls="mf-layout-header",
|
||||
)
|
||||
|
||||
def _mk_footer(self):
|
||||
@@ -208,9 +215,17 @@ class Layout(SingleInstance):
|
||||
Returns:
|
||||
Footer: FastHTML Footer component
|
||||
"""
|
||||
footer_content = self._footer_content if self._footer_content else ""
|
||||
return Footer(
|
||||
footer_content,
|
||||
Div( # left
|
||||
*self._mk_content_wrapper(self.footer_left, horizontal=True, show_group_name=False).children,
|
||||
cls="flex gap-1",
|
||||
id=f"{self._id}_fl"
|
||||
),
|
||||
Div( # right
|
||||
*self._mk_content_wrapper(self.footer_right, horizontal=True, show_group_name=False).children,
|
||||
cls="flex gap-1",
|
||||
id=f"{self._id}_fr"
|
||||
),
|
||||
cls="mf-layout-footer footer sm:footer-horizontal"
|
||||
)
|
||||
|
||||
@@ -277,7 +292,14 @@ class Layout(SingleInstance):
|
||||
|
||||
# Wrap content in scrollable container
|
||||
content_wrapper = Div(
|
||||
*self.right_drawer.get_content(),
|
||||
*[
|
||||
(
|
||||
Div(cls="divider") if index > 0 else None,
|
||||
group_ft,
|
||||
*[item for item in self.right_drawer.get_content()[group_name]]
|
||||
)
|
||||
for index, (group_name, group_ft) in enumerate(self.right_drawer.get_groups())
|
||||
],
|
||||
cls="mf-layout-drawer-content"
|
||||
)
|
||||
|
||||
@@ -290,15 +312,29 @@ class Layout(SingleInstance):
|
||||
)
|
||||
|
||||
def _mk_left_drawer_icon(self):
|
||||
return mk.icon(right_drawer_icon if self._state.left_drawer_open else left_drawer_icon,
|
||||
return mk.icon(left_drawer_contract if self._state.left_drawer_open else left_drawer_expand,
|
||||
id=f"{self._id}_ldi",
|
||||
command=self.commands.toggle_drawer("left"))
|
||||
|
||||
def _mk_right_drawer_icon(self):
|
||||
return mk.icon(right_drawer_icon if self._state.left_drawer_open else left_drawer_icon,
|
||||
return mk.icon(right_drawer_contract if self._state.right_drawer_open else right_drawer_expand,
|
||||
id=f"{self._id}_rdi",
|
||||
command=self.commands.toggle_drawer("right"))
|
||||
|
||||
@staticmethod
|
||||
def _mk_content_wrapper(content: Content, show_group_name: bool = True, horizontal: bool = False):
|
||||
return Div(
|
||||
*[
|
||||
(
|
||||
Div(cls=f"divider {'divider-horizontal' if horizontal else ''}") if index > 0 else None,
|
||||
group_ft if show_group_name else None,
|
||||
*[item for item in content.get_content()[group_name]]
|
||||
)
|
||||
for index, (group_name, group_ft) in enumerate(content.get_groups())
|
||||
],
|
||||
cls="mf-layout-drawer-content"
|
||||
)
|
||||
|
||||
def render(self):
|
||||
"""
|
||||
Render the complete layout.
|
||||
@@ -309,12 +345,13 @@ class Layout(SingleInstance):
|
||||
|
||||
# Wrap everything in a container div
|
||||
return Div(
|
||||
Div(id=f"tt_{self._id}", cls="mf-tooltip-container"), # container for the tooltips
|
||||
self._mk_header(),
|
||||
self._mk_left_drawer(),
|
||||
self._mk_main(),
|
||||
self._mk_right_drawer(),
|
||||
self._mk_footer(),
|
||||
Script(f"initResizer('{self._id}');"),
|
||||
Script(f"initLayout('{self._id}');"),
|
||||
id=self._id,
|
||||
cls="mf-layout",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user