import inspect from dataclasses import dataclass, field from typing import Optional from fasthtml.components import Div from myfasthtml.controls.IconsHelper import IconsHelper from myfasthtml.controls.helpers import mk from myfasthtml.core.dbmanager import DbObject from myfasthtml.core.instances import MultipleInstance @dataclass class MenuConf: fixed_items: list = field(default_factory=list) class MenuState(DbObject): def __init__(self, owner, save_state): with self.initializing(): super().__init__(owner, save_state=save_state) self.last_used: Optional[list] = None class Menu(MultipleInstance): def __init__(self, parent, conf=None, save_state=True, _id=None): super().__init__(parent, _id=_id) self.conf = conf or MenuConf() self._state = MenuState(self, save_state=save_state) self.usable_commands = self._get_parent_commands() def _get_parent_commands(self): commands_obj = self._parent.commands callables = [ name for name in dir(commands_obj) if not name.startswith("_") and callable(attr := getattr(commands_obj, name)) and len(inspect.signature(attr).parameters) == 0 ] return { c.name: c for c in [getattr(commands_obj, name)() for name in callables] } def _mk_menu(self, command_name): if not isinstance(command_name, str): return command_name command = self.usable_commands.get(command_name) return mk.icon(command.icon or IconsHelper.get("QuestionMark"), command=command, tooltip=command.description) def render(self): return Div( Div( *[self._mk_menu(command_name) for command_name in self.conf.fixed_items], *( Div("|"), *[self._mk_menu(command_name) for command_name in self._state.last_used[:3]] ) if self._state.last_used else [], cls="flex mb-1" ), id=self._id ) def __ft__(self): return self.render()