Added menu management

This commit is contained in:
2026-02-22 22:01:26 +01:00
parent b5abb59332
commit 2af43f357d
5 changed files with 84 additions and 16 deletions

View File

@@ -7,10 +7,12 @@ from fasthtml.components import Div
from fasthtml.xtend import Script
from myfasthtml.controls.BaseCommands import BaseCommands
from myfasthtml.controls.Menu import Menu, MenuConf
from myfasthtml.controls.Query import Query, QueryConf
from myfasthtml.core.commands import Command
from myfasthtml.core.dbmanager import DbObject
from myfasthtml.core.instances import MultipleInstance
from myfasthtml.icons.fluent import arrow_reset20_regular
logger = logging.getLogger("HierarchicalCanvasGraph")
@@ -91,10 +93,11 @@ class Commands(BaseCommands):
This command can be used to fix stuck/frozen canvas by resetting zoom/pan state.
"""
return Command(
"ResetTransform",
"ResetView",
"Reset view transform",
self._owner,
self._owner.handle_reset_view
self._owner.handle_reset_view,
icon=arrow_reset20_regular
).htmx(target=f"#{self._id}")
@@ -136,6 +139,9 @@ class HierarchicalCanvasGraph(MultipleInstance):
self._query.bind_command("QueryChanged", self.commands.apply_filter())
self._query.bind_command("CancelQuery", self.commands.apply_filter())
# Add Menu
self._menu = Menu(self, conf=MenuConf(["ResetView"]))
logger.debug(f"HierarchicalCanvasGraph created with id={self._id}, "
f"nodes={len(conf.nodes)}, edges={len(conf.edges)}")
@@ -341,8 +347,11 @@ class HierarchicalCanvasGraph(MultipleInstance):
options_json = json.dumps(options, indent=2)
return Div(
# Query filter bar
self._query,
Div(
self._query,
self._menu,
cls="flex justify-between m-2"
),
# Canvas element (sized by JS to fill container)
Div(

View File

@@ -13,6 +13,7 @@ default_icons = {
False: checkbox_unchecked20_regular,
"Brain": brain_circuit20_regular,
"QuestionMark" : question20_regular,
# TreeView icons
"TreeViewFolder": folder20_regular,

View File

@@ -1,11 +1,10 @@
from dataclasses import dataclass
from fasthtml.components import Div, Button
from fasthtml.components import Div
from myfasthtml.controls.HierarchicalCanvasGraph import HierarchicalCanvasGraph, HierarchicalCanvasGraphConf
from myfasthtml.controls.Panel import Panel
from myfasthtml.controls.Properties import Properties
from myfasthtml.controls.helpers import mk
from myfasthtml.core.commands import Command
from myfasthtml.core.instances import SingleInstance, UniqueInstance, MultipleInstance, InstancesManager
@@ -43,20 +42,12 @@ class InstancesDebugger(SingleInstance):
}
)
self._canvas_graph = HierarchicalCanvasGraph(self, conf=graph_conf, _id="-canvas-graph")
# Debug button to reset transform
reset_btn = mk.button(
"🔄 Reset View",
command=self._canvas_graph.commands.reset_view(),
cls="btn btn-sm btn-ghost"
)
main_content = Div(
Div(reset_btn, cls="p-2 flex justify-end bg-base-200"),
self._canvas_graph,
cls="flex flex-col h-full"
)
return self._panel.set_main(main_content)
def on_select_node(self, node_id=None, label=None, kind=None, type=None):

View File

@@ -0,0 +1,65 @@
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(getattr(commands_obj, name))
]
return {
c.name: c for c in [getattr(commands_obj, name)() for name in callables]
}
def _mk_menu(self, 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"
),
id=self._id
)
def __ft__(self):
return self.render()