Compare commits
2 Commits
bb8752233e
...
a6ab4b2a68
| Author | SHA1 | Date | |
|---|---|---|---|
| a6ab4b2a68 | |||
| 84c63f0c5a |
11
src/app.py
11
src/app.py
@@ -36,7 +36,7 @@ def index(session):
|
||||
layout = Layout(session_instance, "Testing Layout")
|
||||
layout.set_footer("Goodbye World")
|
||||
|
||||
tabs_manager = TabsManager(layout, _id=f"{TabsManager.compute_prefix()}-main")
|
||||
tabs_manager = TabsManager(layout, _id=f"-tabs_manager")
|
||||
btn_show_right_drawer = mk.button("show",
|
||||
command=layout.commands.toggle_drawer("right"),
|
||||
id="btn_show_right_drawer_id")
|
||||
@@ -55,7 +55,7 @@ def index(session):
|
||||
|
||||
btn_file_upload = mk.label("Upload",
|
||||
icon=folder_open20_regular,
|
||||
command=tabs_manager.commands.add_tab("File Open", FileUpload(layout)),
|
||||
command=tabs_manager.commands.add_tab("File Open", FileUpload(layout, _id="-file_upload")),
|
||||
id="file_upload_id")
|
||||
|
||||
layout.header_left.add(tabs_manager.add_tab_btn())
|
||||
@@ -64,8 +64,11 @@ def index(session):
|
||||
layout.left_drawer.add(btn_show_commands_debugger, "Debugger")
|
||||
layout.left_drawer.add(btn_file_upload, "Test")
|
||||
layout.set_main(tabs_manager)
|
||||
keyboard = Keyboard(layout).add("ctrl+o", tabs_manager.commands.add_tab("File Open", FileUpload(layout)))
|
||||
keyboard.add("ctrl+n", tabs_manager.commands.add_tab("File Open", FileUpload(layout)))
|
||||
keyboard = Keyboard(layout, _id="-keyboard").add("ctrl+o",
|
||||
tabs_manager.commands.add_tab("File Open",
|
||||
FileUpload(layout,
|
||||
_id="-file_upload")))
|
||||
keyboard.add("ctrl+n", tabs_manager.commands.add_tab("File Open", FileUpload(layout, _id="-file_upload")))
|
||||
return layout, keyboard
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class InstancesDebugger(SingleInstance):
|
||||
nodes, edges = from_parent_child_list(
|
||||
instances,
|
||||
id_getter=lambda x: x.get_full_id(),
|
||||
label_getter=lambda x: f"{s_name(x.get_session())}-{x.get_prefix()}",
|
||||
label_getter=lambda x: f"{x.get_id()}",
|
||||
parent_getter=lambda x: x.get_full_parent_id()
|
||||
)
|
||||
for edge in edges:
|
||||
@@ -23,7 +23,7 @@ class InstancesDebugger(SingleInstance):
|
||||
for node in nodes:
|
||||
node["shape"] = "box"
|
||||
|
||||
vis_network = VisNetwork(self, nodes=nodes, edges=edges)
|
||||
vis_network = VisNetwork(self, nodes=nodes, edges=edges, _id="-vis")
|
||||
# vis_network.add_to_options(physics={"wind": {"x": 0, "y": 1}})
|
||||
return vis_network
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Callable, Any
|
||||
from fasthtml.components import *
|
||||
|
||||
from myfasthtml.controls.BaseCommands import BaseCommands
|
||||
from myfasthtml.controls.helpers import Ids, mk
|
||||
from myfasthtml.controls.helpers import mk
|
||||
from myfasthtml.core.commands import Command
|
||||
from myfasthtml.core.instances import MultipleInstance, BaseInstance
|
||||
from myfasthtml.core.matching_utils import subsequence_matching, fuzzy_matching
|
||||
|
||||
@@ -84,7 +84,8 @@ class TabsManager(MultipleInstance):
|
||||
self._search = Search(self,
|
||||
items=self._get_tab_list(),
|
||||
get_attr=lambda x: x["label"],
|
||||
template=self._mk_tab_button)
|
||||
template=self._mk_tab_button,
|
||||
_id="-search")
|
||||
logger.debug(f"TabsManager created with id: {self._id}")
|
||||
logger.debug(f" tabs : {self._get_ordered_tabs()}")
|
||||
logger.debug(f" active tab : {self._state.active_tab}")
|
||||
|
||||
@@ -45,7 +45,7 @@ class BaseCommand:
|
||||
def execute(self, client_response: dict = None):
|
||||
raise NotImplementedError
|
||||
|
||||
def htmx(self, target="this", swap="outerHTML", trigger=None):
|
||||
def htmx(self, target: Optional[str] = "this", swap="outerHTML", trigger=None):
|
||||
# Note that the default value is the same than in get_htmx_params()
|
||||
if target is None:
|
||||
self._htmx_extra["hx-swap"] = "none"
|
||||
@@ -180,6 +180,15 @@ class Command(BaseCommand):
|
||||
return [ret] + ret_from_bindings
|
||||
|
||||
|
||||
class LambdaCommand(Command):
|
||||
def __init__(self, delegate, name="LambdaCommand", description="Lambda Command"):
|
||||
super().__init__(name, description, delegate)
|
||||
self.htmx(target=None)
|
||||
|
||||
def execute(self, client_response: dict = None):
|
||||
return self.callback(client_response)
|
||||
|
||||
|
||||
class CommandsManager:
|
||||
commands = {}
|
||||
|
||||
|
||||
@@ -31,9 +31,8 @@ class BaseInstance:
|
||||
session = args[1] if len(args) > 1 and isinstance(args[1], dict) else kwargs.get("session", None)
|
||||
_id = args[2] if len(args) > 2 and isinstance(args[2], str) else kwargs.get("_id", None)
|
||||
|
||||
# Compute _id if not provided
|
||||
if _id is None:
|
||||
_id = cls.compute_id()
|
||||
# Compute _id
|
||||
_id = cls.compute_id(_id, parent)
|
||||
|
||||
if session is None:
|
||||
if parent is not None:
|
||||
@@ -68,7 +67,7 @@ class BaseInstance:
|
||||
|
||||
self._parent = parent
|
||||
self._session = session or (parent.get_session() if parent else None)
|
||||
self._id = _id or self.compute_id()
|
||||
self._id = self.compute_id(_id, parent)
|
||||
self._prefix = self._id if isinstance(self, (UniqueInstance, SingleInstance)) else self.compute_prefix()
|
||||
|
||||
if auto_register:
|
||||
@@ -98,12 +97,18 @@ class BaseInstance:
|
||||
return f"mf-{pascal_to_snake(cls.__name__)}"
|
||||
|
||||
@classmethod
|
||||
def compute_id(cls):
|
||||
prefix = cls.compute_prefix()
|
||||
if issubclass(cls, SingleInstance):
|
||||
_id = prefix
|
||||
else:
|
||||
_id = f"{prefix}-{str(uuid.uuid4())}"
|
||||
def compute_id(cls, _id: Optional[str], parent: Optional['BaseInstance']):
|
||||
if _id is None:
|
||||
prefix = cls.compute_prefix()
|
||||
if issubclass(cls, SingleInstance):
|
||||
_id = prefix
|
||||
else:
|
||||
_id = f"{prefix}-{str(uuid.uuid4())}"
|
||||
return _id
|
||||
|
||||
if _id.startswith("-") and parent is not None:
|
||||
return f"{parent.get_prefix()}{_id}"
|
||||
|
||||
return _id
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from collections.abc import Callable
|
||||
|
||||
ROOT_COLOR = "#ff9999"
|
||||
GHOST_COLOR = "#cccccc"
|
||||
|
||||
def from_nested_dict(trees: list[dict]) -> tuple[list, list]:
|
||||
"""
|
||||
@@ -147,8 +149,8 @@ def from_parent_child_list(
|
||||
id_getter: Callable = None,
|
||||
label_getter: Callable = None,
|
||||
parent_getter: Callable = None,
|
||||
ghost_color: str = "#cccccc",
|
||||
root_color: str | None = "#ff9999"
|
||||
ghost_color: str = GHOST_COLOR,
|
||||
root_color: str | None = ROOT_COLOR
|
||||
) -> tuple[list, list]:
|
||||
"""
|
||||
Convert a list of items with parent references to vis.js nodes and edges format.
|
||||
|
||||
@@ -5,7 +5,7 @@ import pytest
|
||||
from fasthtml.components import Button, Div
|
||||
from myutils.observable import make_observable, bind
|
||||
|
||||
from myfasthtml.core.commands import Command, CommandsManager
|
||||
from myfasthtml.core.commands import Command, CommandsManager, LambdaCommand
|
||||
from myfasthtml.core.constants import ROUTE_ROOT, Routes
|
||||
from myfasthtml.test.matcher import matches
|
||||
|
||||
@@ -183,3 +183,14 @@ class TestCommandExecute:
|
||||
assert "hx-swap-oob" not in res[0].attrs
|
||||
assert "hx-swap-oob" not in res[1].attrs
|
||||
assert "hx-swap-oob" not in res[3].attrs
|
||||
|
||||
|
||||
class TestLambaCommand:
|
||||
|
||||
def test_i_can_create_a_command_from_lambda(self):
|
||||
command = LambdaCommand(lambda resp: "Hello World")
|
||||
assert command.execute() == "Hello World"
|
||||
|
||||
def test_by_default_target_is_none(self):
|
||||
command = LambdaCommand(lambda resp: "Hello World")
|
||||
assert command.get_htmx_params()["hx-swap"] == "none"
|
||||
|
||||
@@ -129,9 +129,9 @@ class TestBaseInstance:
|
||||
assert instance.get_id() is not None
|
||||
assert instance.get_id().startswith("mf-base_instance-")
|
||||
|
||||
def test_i_can_get_prefix_from_class_name(self):
|
||||
def test_i_can_get_prefix_from_class_name(self, session):
|
||||
"""Test that get_prefix() returns the correct snake_case prefix."""
|
||||
prefix = BaseInstance.get_prefix()
|
||||
prefix = BaseInstance(None, session).get_prefix()
|
||||
|
||||
assert prefix == "mf-base_instance"
|
||||
|
||||
@@ -182,7 +182,7 @@ class TestSingleInstance:
|
||||
instance = SingleInstance(parent=None, session=session)
|
||||
|
||||
assert instance.get_id() == "mf-single_instance"
|
||||
assert instance.get_id() == SingleInstance.get_prefix()
|
||||
assert instance.get_prefix() == "mf-single_instance"
|
||||
|
||||
|
||||
class TestSingleInstanceSubclass:
|
||||
@@ -249,6 +249,18 @@ class TestMultipleInstance:
|
||||
instance2 = MultipleInstance(parent=root_instance, session=session, _id=custom_id)
|
||||
|
||||
assert instance1 is instance2
|
||||
|
||||
def test_key_prefixed_by_underscore_uses_the_parent_id_as_prefix(self, root_instance):
|
||||
"""Test that key prefixed by underscore uses the parent id as prefix."""
|
||||
instance = MultipleInstance(parent=root_instance, _id="-test_id")
|
||||
|
||||
assert instance.get_id() == f"{root_instance.get_id()}-test_id"
|
||||
|
||||
def test_no_parent_id_as_prefix_if_parent_is_none(self, session, root_instance):
|
||||
"""Test that key prefixed by underscore does not use the parent id as prefix if parent is None."""
|
||||
instance = MultipleInstance(parent=None, session=session, _id="-test_id")
|
||||
|
||||
assert instance.get_id() == "-test_id"
|
||||
|
||||
|
||||
class TestMultipleInstanceSubclass:
|
||||
@@ -295,9 +307,9 @@ class TestMultipleInstanceSubclass:
|
||||
with pytest.raises(TypeError):
|
||||
MultipleInstance(parent=root_instance, _id=instance1.get_id())
|
||||
|
||||
def test_i_can_get_correct_prefix_for_multiple_subclass(self):
|
||||
def test_i_can_get_correct_prefix_for_multiple_subclass(self, root_instance):
|
||||
"""Test that subclass has correct auto-generated prefix."""
|
||||
prefix = SubMultipleInstance.get_prefix()
|
||||
prefix = SubMultipleInstance(root_instance).get_prefix()
|
||||
|
||||
assert prefix == "mf-sub_multiple_instance"
|
||||
|
||||
|
||||
@@ -405,7 +405,7 @@ class TestFromParentChildList:
|
||||
|
||||
ghost_node = [n for n in nodes if n["id"] == "ghost"][0]
|
||||
assert "color" in ghost_node
|
||||
assert ghost_node["color"] == "#ff9999"
|
||||
assert ghost_node["color"] == "#cccccc"
|
||||
|
||||
def test_i_can_use_custom_ghost_color(self):
|
||||
"""Test that custom ghost_color parameter is applied."""
|
||||
|
||||
Reference in New Issue
Block a user