221 lines
6.2 KiB
Python
221 lines
6.2 KiB
Python
from dataclasses import dataclass
|
|
from typing import Literal
|
|
|
|
from fasthtml.components import Div
|
|
from fasthtml.xtend import Script
|
|
|
|
from myfasthtml.controls.BaseCommands import BaseCommands
|
|
from myfasthtml.controls.helpers import mk
|
|
from myfasthtml.core.commands import Command
|
|
from myfasthtml.core.dbmanager import DbObject
|
|
from myfasthtml.core.instances import MultipleInstance
|
|
from myfasthtml.icons.fluent_p1 import more_horizontal20_regular
|
|
from myfasthtml.icons.fluent_p2 import subtract20_regular
|
|
|
|
|
|
class PanelIds:
|
|
def __init__(self, owner):
|
|
self._owner = owner
|
|
|
|
@property
|
|
def main(self):
|
|
return f"{self._owner.get_id()}_m"
|
|
|
|
@property
|
|
def right(self):
|
|
""" Right panel's content"""
|
|
return f"{self._owner.get_id()}_cr"
|
|
|
|
@property
|
|
def left(self):
|
|
""" Left panel's content"""
|
|
return f"{self._owner.get_id()}_cl"
|
|
|
|
def panel(self, side: Literal["left", "right"]):
|
|
return f"{self._owner.get_id()}_pl" if side == "left" else f"{self._owner.get_id()}_pr"
|
|
|
|
def content(self, side: Literal["left", "right"]):
|
|
return self.left if side == "left" else self.right
|
|
|
|
|
|
@dataclass
|
|
class PanelConf:
|
|
left: bool = False
|
|
right: bool = True
|
|
|
|
|
|
class PanelState(DbObject):
|
|
def __init__(self, owner, name=None):
|
|
super().__init__(owner, name=name)
|
|
with self.initializing():
|
|
self.left_visible: bool = True
|
|
self.right_visible: bool = True
|
|
self.left_width: int = 250
|
|
self.right_width: int = 250
|
|
|
|
|
|
class Commands(BaseCommands):
|
|
def toggle_side(self, side: Literal["left", "right"], visible: bool = None):
|
|
return Command("TogglePanelSide",
|
|
f"Toggle {side} side panel",
|
|
self._owner,
|
|
self._owner.toggle_side,
|
|
args=[side, visible]).htmx(target=f"#{self._owner.get_ids().panel(side)}")
|
|
|
|
def update_side_width(self, side: Literal["left", "right"]):
|
|
"""
|
|
Create a command to update panel's side width.
|
|
|
|
Args:
|
|
side: Which panel's side to update ("left" or "right")
|
|
|
|
Returns:
|
|
Command: Command object for updating panel's side width
|
|
"""
|
|
return Command(f"UpdatePanelSideWidth_{side}",
|
|
f"Update {side} side panel width",
|
|
self._owner,
|
|
self._owner.update_side_width,
|
|
args=[side]).htmx(target=f"#{self._owner.get_ids().panel(side)}")
|
|
|
|
|
|
class Panel(MultipleInstance):
|
|
"""
|
|
Represents a user interface panel that supports customizable left, main, and right components.
|
|
|
|
The `Panel` class is used to create and manage a panel layout with optional left, main,
|
|
and right sections. It provides functionality to set the components of the panel, toggle
|
|
sides, and adjust the width of the sides dynamically. The class also handles rendering
|
|
the panel with appropriate HTML elements and JavaScript for interactivity.
|
|
"""
|
|
|
|
def __init__(self, parent, conf=None, _id=None):
|
|
super().__init__(parent, _id=_id)
|
|
self.conf = conf or PanelConf()
|
|
self.commands = Commands(self)
|
|
self._state = PanelState(self)
|
|
self._main = None
|
|
self._right = None
|
|
self._left = None
|
|
self._ids = PanelIds(self)
|
|
|
|
def get_ids(self):
|
|
return self._ids
|
|
|
|
def update_side_width(self, side, width):
|
|
if side == "left":
|
|
self._state.left_width = width
|
|
else:
|
|
self._state.right_width = width
|
|
|
|
return self._mk_panel(side)
|
|
|
|
def toggle_side(self, side, visible):
|
|
if side == "left":
|
|
self._state.left_visible = visible
|
|
else:
|
|
self._state.right_visible = visible
|
|
|
|
return self._mk_panel(side), self._mk_show_icon(side)
|
|
|
|
def set_main(self, main):
|
|
self._main = main
|
|
return self
|
|
|
|
def set_right(self, right):
|
|
self._right = right
|
|
return Div(self._right, id=self._ids.right)
|
|
|
|
def set_left(self, left):
|
|
self._left = left
|
|
return Div(self._left, id=self._ids.left)
|
|
|
|
def _mk_panel(self, side: Literal["left", "right"]):
|
|
enabled = self.conf.left if side == "left" else self.conf.right
|
|
if not enabled:
|
|
return None
|
|
|
|
visible = self._state.left_visible if side == "left" else self._state.right_visible
|
|
content = self._right if side == "right" else self._left
|
|
|
|
resizer = Div(
|
|
cls=f"mf-resizer mf-resizer-{side}",
|
|
data_command_id=self.commands.update_side_width(side).id,
|
|
data_side=side
|
|
)
|
|
|
|
hide_icon = mk.icon(
|
|
subtract20_regular,
|
|
size=20,
|
|
command=self.commands.toggle_side(side, False),
|
|
cls="mf-panel-hide-icon"
|
|
)
|
|
|
|
panel_cls = f"mf-panel-{side}"
|
|
if not visible:
|
|
panel_cls += " mf-hidden"
|
|
|
|
# Left panel: content then resizer (resizer on the right)
|
|
# Right panel: resizer then content (resizer on the left)
|
|
if side == "left":
|
|
return Div(
|
|
hide_icon,
|
|
Div(content, id=self._ids.content(side)),
|
|
resizer,
|
|
cls=panel_cls,
|
|
id=self._ids.panel(side)
|
|
)
|
|
else:
|
|
return Div(
|
|
resizer,
|
|
hide_icon,
|
|
Div(content, id=self._ids.content(side)),
|
|
cls=panel_cls,
|
|
id=self._ids.panel(side)
|
|
)
|
|
|
|
def _mk_main(self):
|
|
return Div(
|
|
self._mk_show_icon("left"),
|
|
Div(self._main, id=self._ids.main, cls="mf-panel-main"),
|
|
self._mk_show_icon("right"),
|
|
cls="mf-panel-main"
|
|
),
|
|
|
|
def _mk_show_icon(self, side: Literal["left", "right"]):
|
|
"""
|
|
Create show icon for a panel side if it's hidden.
|
|
|
|
Args:
|
|
side: Which panel side ("left" or "right")
|
|
|
|
Returns:
|
|
Div with icon if panel is hidden, None otherwise
|
|
"""
|
|
enabled = self.conf.left if side == "left" else self.conf.right
|
|
if not enabled:
|
|
return None
|
|
|
|
is_visible = self._state.left_visible if side == "left" else self._state.right_visible
|
|
icon_cls = "hidden" if is_visible else f"mf-panel-show-icon mf-panel-show-icon-{side}"
|
|
|
|
return mk.icon(
|
|
more_horizontal20_regular,
|
|
command=self.commands.toggle_side(side, True),
|
|
cls=icon_cls,
|
|
id=f"{self._id}_show_{side}"
|
|
)
|
|
|
|
def render(self):
|
|
return Div(
|
|
self._mk_panel("left"),
|
|
self._mk_main(),
|
|
self._mk_panel("right"),
|
|
Script(f"initResizer('{self._id}');"),
|
|
cls="mf-panel",
|
|
id=self._id,
|
|
)
|
|
|
|
def __ft__(self):
|
|
return self.render()
|