Compare commits
2 Commits
3aa36a91aa
...
b26abc4257
| Author | SHA1 | Date | |
|---|---|---|---|
| b26abc4257 | |||
| 045f01b48a |
@@ -17,6 +17,7 @@ class Commands(BaseCommands):
|
|||||||
def update_boundaries(self):
|
def update_boundaries(self):
|
||||||
return Command(f"{self._prefix}UpdateBoundaries",
|
return Command(f"{self._prefix}UpdateBoundaries",
|
||||||
"Update component boundaries",
|
"Update component boundaries",
|
||||||
|
self._owner,
|
||||||
self._owner.update_boundaries).htmx(target=f"{self._owner.get_id()}")
|
self._owner.update_boundaries).htmx(target=f"{self._owner.get_id()}")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastcore.basics import NotStr
|
||||||
from fasthtml.components import Div
|
from fasthtml.components import Div
|
||||||
|
|
||||||
from myfasthtml.controls.BaseCommands import BaseCommands
|
from myfasthtml.controls.BaseCommands import BaseCommands
|
||||||
from myfasthtml.controls.datagrid_objects import DataGridColumnState, DataGridRowState, DataGridFooterConf, \
|
from myfasthtml.controls.datagrid_objects import DataGridColumnState, DataGridRowState, \
|
||||||
DatagridSelectionState, DataGridHeaderFooterConf, DatagridEditionState
|
DatagridSelectionState, DataGridHeaderFooterConf, DatagridEditionState
|
||||||
from myfasthtml.core.dbmanager import DbObject
|
from myfasthtml.core.dbmanager import DbObject
|
||||||
from myfasthtml.core.instances import MultipleInstance
|
from myfasthtml.core.instances import MultipleInstance
|
||||||
@@ -24,6 +25,7 @@ class DatagridState(DbObject):
|
|||||||
self.filtered: dict = {}
|
self.filtered: dict = {}
|
||||||
self.edition: DatagridEditionState = DatagridEditionState()
|
self.edition: DatagridEditionState = DatagridEditionState()
|
||||||
self.selection: DatagridSelectionState = DatagridSelectionState()
|
self.selection: DatagridSelectionState = DatagridSelectionState()
|
||||||
|
self.html = None
|
||||||
|
|
||||||
|
|
||||||
class DatagridSettings(DbObject):
|
class DatagridSettings(DbObject):
|
||||||
@@ -46,13 +48,17 @@ class Commands(BaseCommands):
|
|||||||
class DataGrid(MultipleInstance):
|
class DataGrid(MultipleInstance):
|
||||||
def __init__(self, parent, settings=None, _id=None):
|
def __init__(self, parent, settings=None, _id=None):
|
||||||
super().__init__(parent, _id=_id)
|
super().__init__(parent, _id=_id)
|
||||||
self._settings = DatagridSettings(self).update(settings)
|
self._settings = DatagridSettings(self)
|
||||||
self._state = DatagridState(self)
|
self._state = DatagridState(self)
|
||||||
self.commands = Commands(self)
|
self.commands = Commands(self)
|
||||||
|
|
||||||
|
def set_html(self, html):
|
||||||
|
self._state.html = html
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
return Div(
|
return Div(
|
||||||
self._id
|
NotStr(self._state.html),
|
||||||
|
id=self._id
|
||||||
)
|
)
|
||||||
|
|
||||||
def __ft__(self):
|
def __ft__(self):
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from fastcore.basics import NotStr
|
|
||||||
from fasthtml.components import Div
|
from fasthtml.components import Div
|
||||||
|
|
||||||
from myfasthtml.controls.BaseCommands import BaseCommands
|
from myfasthtml.controls.BaseCommands import BaseCommands
|
||||||
|
from myfasthtml.controls.DataGrid import DataGrid
|
||||||
from myfasthtml.controls.FileUpload import FileUpload
|
from myfasthtml.controls.FileUpload import FileUpload
|
||||||
|
from myfasthtml.controls.Panel import Panel
|
||||||
from myfasthtml.controls.TabsManager import TabsManager
|
from myfasthtml.controls.TabsManager import TabsManager
|
||||||
from myfasthtml.controls.TreeView import TreeView, TreeNode
|
from myfasthtml.controls.TreeView import TreeView, TreeNode
|
||||||
from myfasthtml.controls.helpers import mk
|
from myfasthtml.controls.helpers import mk
|
||||||
@@ -36,16 +37,19 @@ class Commands(BaseCommands):
|
|||||||
def upload_from_source(self):
|
def upload_from_source(self):
|
||||||
return Command("UploadFromSource",
|
return Command("UploadFromSource",
|
||||||
"Upload from source",
|
"Upload from source",
|
||||||
|
self._owner,
|
||||||
self._owner.upload_from_source).htmx(target=None)
|
self._owner.upload_from_source).htmx(target=None)
|
||||||
|
|
||||||
def new_grid(self):
|
def new_grid(self):
|
||||||
return Command("NewGrid",
|
return Command("NewGrid",
|
||||||
"New grid",
|
"New grid",
|
||||||
|
self._owner,
|
||||||
self._owner.new_grid)
|
self._owner.new_grid)
|
||||||
|
|
||||||
def open_from_excel(self, tab_id, file_upload):
|
def open_from_excel(self, tab_id, file_upload):
|
||||||
return Command("OpenFromExcel",
|
return Command("OpenFromExcel",
|
||||||
"Open from Excel",
|
"Open from Excel",
|
||||||
|
self._owner,
|
||||||
self._owner.open_from_excel,
|
self._owner.open_from_excel,
|
||||||
tab_id,
|
tab_id,
|
||||||
file_upload).htmx(target=f"#{self._owner._tree.get_id()}")
|
file_upload).htmx(target=f"#{self._owner._tree.get_id()}")
|
||||||
@@ -53,6 +57,7 @@ class Commands(BaseCommands):
|
|||||||
def clear_tree(self):
|
def clear_tree(self):
|
||||||
return Command("ClearTree",
|
return Command("ClearTree",
|
||||||
"Clear tree",
|
"Clear tree",
|
||||||
|
self._owner,
|
||||||
self._owner.clear_tree).htmx(target=f"#{self._owner._tree.get_id()}")
|
self._owner.clear_tree).htmx(target=f"#{self._owner._tree.get_id()}")
|
||||||
|
|
||||||
|
|
||||||
@@ -72,8 +77,10 @@ class DataGridsManager(MultipleInstance):
|
|||||||
|
|
||||||
def open_from_excel(self, tab_id, file_upload: FileUpload):
|
def open_from_excel(self, tab_id, file_upload: FileUpload):
|
||||||
excel_content = file_upload.get_content()
|
excel_content = file_upload.get_content()
|
||||||
df = pd.read_excel(excel_content)
|
df = pd.read_excel(excel_content, file_upload.get_sheet_name())
|
||||||
content = df.to_html(index=False)
|
html = df.to_html(index=False)
|
||||||
|
dg = DataGrid(self._tabs_manager)
|
||||||
|
dg.set_html(html)
|
||||||
document = DocumentDefinition(
|
document = DocumentDefinition(
|
||||||
namespace=file_upload.get_file_basename(),
|
namespace=file_upload.get_file_basename(),
|
||||||
name=file_upload.get_sheet_name(),
|
name=file_upload.get_sheet_name(),
|
||||||
@@ -85,7 +92,7 @@ class DataGridsManager(MultipleInstance):
|
|||||||
parent_id = self._tree.ensure_path(document.namespace)
|
parent_id = self._tree.ensure_path(document.namespace)
|
||||||
tree_node = TreeNode(label=document.name, type="excel", parent=parent_id)
|
tree_node = TreeNode(label=document.name, type="excel", parent=parent_id)
|
||||||
self._tree.add_node(tree_node, parent_id=parent_id)
|
self._tree.add_node(tree_node, parent_id=parent_id)
|
||||||
return self._mk_tree(), self._tabs_manager.change_tab_content(tab_id, document.name, NotStr(content))
|
return self._mk_tree(), self._tabs_manager.change_tab_content(tab_id, document.name, Panel(self).set_main(dg))
|
||||||
|
|
||||||
def clear_tree(self):
|
def clear_tree(self):
|
||||||
self._state.elements = []
|
self._state.elements = []
|
||||||
|
|||||||
@@ -10,10 +10,16 @@ from myfasthtml.core.instances import MultipleInstance
|
|||||||
|
|
||||||
class Commands(BaseCommands):
|
class Commands(BaseCommands):
|
||||||
def close(self):
|
def close(self):
|
||||||
return Command("Close", "Close Dropdown", self._owner.close).htmx(target=f"#{self._owner.get_id()}-content")
|
return Command("Close",
|
||||||
|
"Close Dropdown",
|
||||||
|
self._owner,
|
||||||
|
self._owner.close).htmx(target=f"#{self._owner.get_id()}-content")
|
||||||
|
|
||||||
def click(self):
|
def click(self):
|
||||||
return Command("Click", "Click on Dropdown", self._owner.on_click).htmx(target=f"#{self._owner.get_id()}-content")
|
return Command("Click",
|
||||||
|
"Click on Dropdown",
|
||||||
|
self._owner,
|
||||||
|
self._owner.on_click).htmx(target=f"#{self._owner.get_id()}-content")
|
||||||
|
|
||||||
|
|
||||||
class DropdownState:
|
class DropdownState:
|
||||||
|
|||||||
@@ -34,10 +34,16 @@ class Commands(BaseCommands):
|
|||||||
super().__init__(owner)
|
super().__init__(owner)
|
||||||
|
|
||||||
def on_file_uploaded(self):
|
def on_file_uploaded(self):
|
||||||
return Command("UploadFile", "Upload file", self._owner.upload_file).htmx(target=f"#sn_{self._id}")
|
return Command("UploadFile",
|
||||||
|
"Upload file",
|
||||||
|
self._owner,
|
||||||
|
self._owner.upload_file).htmx(target=f"#sn_{self._id}")
|
||||||
|
|
||||||
def on_sheet_selected(self):
|
def on_sheet_selected(self):
|
||||||
return Command("SheetSelected", "Sheet selected", self._owner.select_sheet).htmx(target=f"#sn_{self._id}")
|
return Command("SheetSelected",
|
||||||
|
"Sheet selected",
|
||||||
|
self._owner,
|
||||||
|
self._owner.select_sheet).htmx(target=f"#sn_{self._id}")
|
||||||
|
|
||||||
|
|
||||||
class FileUpload(MultipleInstance):
|
class FileUpload(MultipleInstance):
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class InstancesDebugger(SingleInstance):
|
|||||||
self._panel = Panel(self, _id="-panel")
|
self._panel = Panel(self, _id="-panel")
|
||||||
self._command = Command("ShowInstance",
|
self._command = Command("ShowInstance",
|
||||||
"Display selected Instance",
|
"Display selected Instance",
|
||||||
|
self,
|
||||||
self.on_network_event).htmx(target=f"#{self._panel.get_id()}_r")
|
self.on_network_event).htmx(target=f"#{self._panel.get_id()}_r")
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
|
|||||||
@@ -37,7 +37,10 @@ class LayoutState(DbObject):
|
|||||||
|
|
||||||
class Commands(BaseCommands):
|
class Commands(BaseCommands):
|
||||||
def toggle_drawer(self, side: Literal["left", "right"]):
|
def toggle_drawer(self, side: Literal["left", "right"]):
|
||||||
return Command("ToggleDrawer", f"Toggle {side} layout drawer", self._owner.toggle_drawer, side)
|
return Command("ToggleDrawer",
|
||||||
|
f"Toggle {side} layout drawer",
|
||||||
|
self._owner,
|
||||||
|
self._owner.toggle_drawer, side)
|
||||||
|
|
||||||
def update_drawer_width(self, side: Literal["left", "right"], width: int = None):
|
def update_drawer_width(self, side: Literal["left", "right"], width: int = None):
|
||||||
"""
|
"""
|
||||||
@@ -50,12 +53,11 @@ class Commands(BaseCommands):
|
|||||||
Returns:
|
Returns:
|
||||||
Command: Command object for updating drawer width
|
Command: Command object for updating drawer width
|
||||||
"""
|
"""
|
||||||
return Command(
|
return Command(f"UpdateDrawerWidth_{side}",
|
||||||
f"UpdateDrawerWidth_{side}",
|
f"Update {side} drawer width",
|
||||||
f"Update {side} drawer width",
|
self._owner,
|
||||||
self._owner.update_drawer_width,
|
self._owner.update_drawer_width,
|
||||||
side
|
side)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Layout(SingleInstance):
|
class Layout(SingleInstance):
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ class PanelConf:
|
|||||||
|
|
||||||
class Commands(BaseCommands):
|
class Commands(BaseCommands):
|
||||||
def toggle_side(self, side: Literal["left", "right"]):
|
def toggle_side(self, side: Literal["left", "right"]):
|
||||||
return Command("TogglePanelSide", f"Toggle {side} side panel", self._owner.toggle_side, side)
|
return Command("TogglePanelSide",
|
||||||
|
f"Toggle {side} side panel",
|
||||||
|
self._owner,
|
||||||
|
self._owner.toggle_side,
|
||||||
|
side)
|
||||||
|
|
||||||
def update_side_width(self, side: Literal["left", "right"]):
|
def update_side_width(self, side: Literal["left", "right"]):
|
||||||
"""
|
"""
|
||||||
@@ -29,12 +33,11 @@ class Commands(BaseCommands):
|
|||||||
Returns:
|
Returns:
|
||||||
Command: Command object for updating panel's side width
|
Command: Command object for updating panel's side width
|
||||||
"""
|
"""
|
||||||
return Command(
|
return Command(f"UpdatePanelSideWidth_{side}",
|
||||||
f"UpdatePanelSideWidth_{side}",
|
f"Update {side} side panel width",
|
||||||
f"Update {side} side panel width",
|
self._owner,
|
||||||
self._owner.update_side_width,
|
self._owner.update_side_width,
|
||||||
side
|
side)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Panel(MultipleInstance):
|
class Panel(MultipleInstance):
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ logger = logging.getLogger("Search")
|
|||||||
|
|
||||||
class Commands(BaseCommands):
|
class Commands(BaseCommands):
|
||||||
def search(self):
|
def search(self):
|
||||||
return (Command("Search", f"Search {self._owner.items_names}", self._owner.on_search).
|
return (Command("Search",
|
||||||
htmx(target=f"#{self._owner.get_id()}-results",
|
f"Search {self._owner.items_names}",
|
||||||
trigger="keyup changed delay:300ms",
|
self._owner,
|
||||||
swap="innerHTML"))
|
self._owner.on_search).htmx(target=f"#{self._owner.get_id()}-results",
|
||||||
|
trigger="keyup changed delay:300ms",
|
||||||
|
swap="innerHTML"))
|
||||||
|
|
||||||
|
|
||||||
class Search(MultipleInstance):
|
class Search(MultipleInstance):
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ class Commands(BaseCommands):
|
|||||||
def show_tab(self, tab_id):
|
def show_tab(self, tab_id):
|
||||||
return Command(f"{self._prefix}ShowTab",
|
return Command(f"{self._prefix}ShowTab",
|
||||||
"Activate or show a specific tab",
|
"Activate or show a specific tab",
|
||||||
|
self._owner,
|
||||||
self._owner.show_tab,
|
self._owner.show_tab,
|
||||||
tab_id,
|
tab_id,
|
||||||
True,
|
True,
|
||||||
@@ -68,12 +69,14 @@ class Commands(BaseCommands):
|
|||||||
def close_tab(self, tab_id):
|
def close_tab(self, tab_id):
|
||||||
return Command(f"{self._prefix}CloseTab",
|
return Command(f"{self._prefix}CloseTab",
|
||||||
"Close a specific tab",
|
"Close a specific tab",
|
||||||
|
self._owner,
|
||||||
self._owner.close_tab,
|
self._owner.close_tab,
|
||||||
tab_id).htmx(target=f"#{self._id}-controller", swap="outerHTML")
|
tab_id).htmx(target=f"#{self._id}-controller", swap="outerHTML")
|
||||||
|
|
||||||
def add_tab(self, label: str, component: Any, auto_increment=False):
|
def add_tab(self, label: str, component: Any, auto_increment=False):
|
||||||
return Command(f"{self._prefix}AddTab",
|
return Command(f"{self._prefix}AddTab",
|
||||||
"Add a new tab",
|
"Add a new tab",
|
||||||
|
self._owner,
|
||||||
self._owner.on_new_tab,
|
self._owner.on_new_tab,
|
||||||
label,
|
label,
|
||||||
component,
|
component,
|
||||||
@@ -108,10 +111,10 @@ class TabsManager(MultipleInstance):
|
|||||||
if tab_id not in self._state.tabs:
|
if tab_id not in self._state.tabs:
|
||||||
return Div("Tab not found.")
|
return Div("Tab not found.")
|
||||||
tab_config = self._state.tabs[tab_id]
|
tab_config = self._state.tabs[tab_id]
|
||||||
if tab_config["component_type"] is None:
|
if tab_config["component"] is None:
|
||||||
return Div("Tab content does not support serialization.")
|
return Div("Tab content does not support serialization.")
|
||||||
try:
|
try:
|
||||||
return InstancesManager.get(self._session, tab_config["component_id"])
|
return InstancesManager.get(self._session, tab_config["component"][1])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error while retrieving tab content: {e}")
|
logger.error(f"Error while retrieving tab content: {e}")
|
||||||
return Div("Failed to retrieve tab content.")
|
return Div("Failed to retrieve tab content.")
|
||||||
@@ -381,8 +384,7 @@ class TabsManager(MultipleInstance):
|
|||||||
|
|
||||||
if component_id is not None:
|
if component_id is not None:
|
||||||
for tab_id, tab_data in self._state.tabs.items():
|
for tab_id, tab_data in self._state.tabs.items():
|
||||||
if (tab_data.get('component_type') == component_type and
|
if (tab_data.get('component') == (component_type, component_id) and
|
||||||
tab_data.get('component_id') == component_id and
|
|
||||||
tab_data.get('label') == label):
|
tab_data.get('label') == label):
|
||||||
return tab_id
|
return tab_id
|
||||||
|
|
||||||
@@ -396,16 +398,21 @@ class TabsManager(MultipleInstance):
|
|||||||
|
|
||||||
# Extract component ID if the component has a get_id() method
|
# Extract component ID if the component has a get_id() method
|
||||||
component_type, component_id = None, None
|
component_type, component_id = None, None
|
||||||
|
parent_type, parent_id = None, None
|
||||||
if isinstance(component, BaseInstance):
|
if isinstance(component, BaseInstance):
|
||||||
component_type = component.get_prefix()
|
component_type = component.get_prefix()
|
||||||
component_id = component.get_id()
|
component_id = component.get_id()
|
||||||
|
parent = component.get_parent()
|
||||||
|
if parent:
|
||||||
|
parent_type = parent.get_prefix()
|
||||||
|
parent_id = parent.get_id()
|
||||||
|
|
||||||
# Add tab metadata to state
|
# Add tab metadata to state
|
||||||
state.tabs[tab_id] = {
|
state.tabs[tab_id] = {
|
||||||
'id': tab_id,
|
'id': tab_id,
|
||||||
'label': label,
|
'label': label,
|
||||||
'component_type': component_type,
|
'component': (component_type, component_id) if component_type else None,
|
||||||
'component_id': component_id
|
'component_parent': (parent_type, parent_id) if parent_type else None
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add tab to order list
|
# Add tab to order list
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class TreeNode:
|
|||||||
type: str = "default"
|
type: str = "default"
|
||||||
parent: Optional[str] = None
|
parent: Optional[str] = None
|
||||||
children: list[str] = field(default_factory=list)
|
children: list[str] = field(default_factory=list)
|
||||||
|
bag: Optional[dict] = None # to keep extra info
|
||||||
|
|
||||||
|
|
||||||
class TreeViewState(DbObject):
|
class TreeViewState(DbObject):
|
||||||
@@ -66,74 +67,67 @@ class Commands(BaseCommands):
|
|||||||
|
|
||||||
def toggle_node(self, node_id: str):
|
def toggle_node(self, node_id: str):
|
||||||
"""Create command to expand/collapse a node."""
|
"""Create command to expand/collapse a node."""
|
||||||
return Command(
|
return Command("ToggleNode",
|
||||||
"ToggleNode",
|
f"Toggle node {node_id}",
|
||||||
f"Toggle node {node_id}",
|
self._owner,
|
||||||
self._owner._toggle_node,
|
self._owner._toggle_node,
|
||||||
node_id
|
node_id).htmx(target=f"#{self._owner.get_id()}")
|
||||||
).htmx(target=f"#{self._owner.get_id()}")
|
|
||||||
|
|
||||||
def add_child(self, parent_id: str):
|
def add_child(self, parent_id: str):
|
||||||
"""Create command to add a child node."""
|
"""Create command to add a child node."""
|
||||||
return Command(
|
return Command("AddChild",
|
||||||
"AddChild",
|
f"Add child to {parent_id}",
|
||||||
f"Add child to {parent_id}",
|
self._owner,
|
||||||
self._owner._add_child,
|
self._owner._add_child,
|
||||||
parent_id
|
parent_id).htmx(target=f"#{self._owner.get_id()}")
|
||||||
).htmx(target=f"#{self._owner.get_id()}")
|
|
||||||
|
|
||||||
def add_sibling(self, node_id: str):
|
def add_sibling(self, node_id: str):
|
||||||
"""Create command to add a sibling node."""
|
"""Create command to add a sibling node."""
|
||||||
return Command(
|
return Command("AddSibling",
|
||||||
"AddSibling",
|
f"Add sibling to {node_id}",
|
||||||
f"Add sibling to {node_id}",
|
self._owner,
|
||||||
self._owner._add_sibling,
|
self._owner._add_sibling,
|
||||||
node_id
|
node_id
|
||||||
).htmx(target=f"#{self._owner.get_id()}")
|
).htmx(target=f"#{self._owner.get_id()}")
|
||||||
|
|
||||||
def start_rename(self, node_id: str):
|
def start_rename(self, node_id: str):
|
||||||
"""Create command to start renaming a node."""
|
"""Create command to start renaming a node."""
|
||||||
return Command(
|
return Command("StartRename",
|
||||||
"StartRename",
|
f"Start renaming {node_id}",
|
||||||
f"Start renaming {node_id}",
|
self._owner,
|
||||||
self._owner._start_rename,
|
self._owner._start_rename,
|
||||||
node_id
|
node_id).htmx(target=f"#{self._owner.get_id()}")
|
||||||
).htmx(target=f"#{self._owner.get_id()}")
|
|
||||||
|
|
||||||
def save_rename(self, node_id: str):
|
def save_rename(self, node_id: str):
|
||||||
"""Create command to save renamed node."""
|
"""Create command to save renamed node."""
|
||||||
return Command(
|
return Command("SaveRename",
|
||||||
"SaveRename",
|
f"Save rename for {node_id}",
|
||||||
f"Save rename for {node_id}",
|
self._owner,
|
||||||
self._owner._save_rename,
|
self._owner._save_rename,
|
||||||
node_id
|
node_id).htmx(target=f"#{self._owner.get_id()}")
|
||||||
).htmx(target=f"#{self._owner.get_id()}")
|
|
||||||
|
|
||||||
def cancel_rename(self):
|
def cancel_rename(self):
|
||||||
"""Create command to cancel renaming."""
|
"""Create command to cancel renaming."""
|
||||||
return Command(
|
return Command("CancelRename",
|
||||||
"CancelRename",
|
"Cancel rename",
|
||||||
"Cancel rename",
|
self._owner,
|
||||||
self._owner._cancel_rename
|
self._owner._cancel_rename).htmx(target=f"#{self._owner.get_id()}")
|
||||||
).htmx(target=f"#{self._owner.get_id()}")
|
|
||||||
|
|
||||||
def delete_node(self, node_id: str):
|
def delete_node(self, node_id: str):
|
||||||
"""Create command to delete a node."""
|
"""Create command to delete a node."""
|
||||||
return Command(
|
return Command("DeleteNode",
|
||||||
"DeleteNode",
|
f"Delete node {node_id}",
|
||||||
f"Delete node {node_id}",
|
self._owner,
|
||||||
self._owner._delete_node,
|
self._owner._delete_node,
|
||||||
node_id
|
node_id).htmx(target=f"#{self._owner.get_id()}")
|
||||||
).htmx(target=f"#{self._owner.get_id()}")
|
|
||||||
|
|
||||||
def select_node(self, node_id: str):
|
def select_node(self, node_id: str):
|
||||||
"""Create command to select a node."""
|
"""Create command to select a node."""
|
||||||
return Command(
|
return Command("SelectNode",
|
||||||
"SelectNode",
|
f"Select node {node_id}",
|
||||||
f"Select node {node_id}",
|
self._owner,
|
||||||
self._owner._select_node,
|
self._owner._select_node,
|
||||||
node_id
|
node_id).htmx(target=f"#{self._owner.get_id()}")
|
||||||
).htmx(target=f"#{self._owner.get_id()}")
|
|
||||||
|
|
||||||
|
|
||||||
class TreeView(MultipleInstance):
|
class TreeView(MultipleInstance):
|
||||||
@@ -187,7 +181,7 @@ class TreeView(MultipleInstance):
|
|||||||
self._state.items[node.id] = node
|
self._state.items[node.id] = node
|
||||||
if parent_id is None and node.parent is not None:
|
if parent_id is None and node.parent is not None:
|
||||||
parent_id = node.parent
|
parent_id = node.parent
|
||||||
|
|
||||||
node.parent = parent_id
|
node.parent = parent_id
|
||||||
|
|
||||||
if parent_id and parent_id in self._state.items:
|
if parent_id and parent_id in self._state.items:
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ class UserProfileState:
|
|||||||
|
|
||||||
class Commands(BaseCommands):
|
class Commands(BaseCommands):
|
||||||
def update_dark_mode(self):
|
def update_dark_mode(self):
|
||||||
return Command("UpdateDarkMode", "Set the dark mode", self._owner.update_dark_mode).htmx(target=None)
|
return Command("UpdateDarkMode",
|
||||||
|
"Set the dark mode",
|
||||||
|
self._owner,
|
||||||
|
self._owner.update_dark_mode).htmx(target=None)
|
||||||
|
|
||||||
|
|
||||||
class UserProfile(SingleInstance):
|
class UserProfile(SingleInstance):
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ class BaseCommand:
|
|||||||
:type description: str
|
:type description: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, description, auto_register=True):
|
def __init__(self, name, description, owner=None, auto_register=True):
|
||||||
self.id = uuid.uuid4()
|
self.id = uuid.uuid4()
|
||||||
self.name = name
|
self.name = name
|
||||||
self.description = description
|
self.description = description
|
||||||
|
self.owner = owner
|
||||||
self._htmx_extra = {}
|
self._htmx_extra = {}
|
||||||
self._bindings = []
|
self._bindings = []
|
||||||
self._ft = None
|
self._ft = None
|
||||||
@@ -133,8 +134,8 @@ class Command(BaseCommand):
|
|||||||
:type kwargs: dict
|
:type kwargs: dict
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, description, callback, *args, **kwargs):
|
def __init__(self, name, description, owner, callback, *args, **kwargs):
|
||||||
super().__init__(name, description)
|
super().__init__(name, description, owner=owner)
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
self.callback_parameters = dict(inspect.signature(callback).parameters) if callback else {}
|
self.callback_parameters = dict(inspect.signature(callback).parameters) if callback else {}
|
||||||
self.args = args
|
self.args = args
|
||||||
@@ -202,8 +203,8 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
|
|
||||||
class LambdaCommand(Command):
|
class LambdaCommand(Command):
|
||||||
def __init__(self, delegate, name="LambdaCommand", description="Lambda Command"):
|
def __init__(self, owner, delegate, name="LambdaCommand", description="Lambda Command"):
|
||||||
super().__init__(name, description, delegate)
|
super().__init__(name, description, owner, delegate)
|
||||||
self.htmx(target=None)
|
self.htmx(target=None)
|
||||||
|
|
||||||
def execute(self, client_response: dict = None):
|
def execute(self, client_response: dict = None):
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ class DbObject:
|
|||||||
properties = {}
|
properties = {}
|
||||||
if args:
|
if args:
|
||||||
arg = args[0]
|
arg = args[0]
|
||||||
|
if arg is None:
|
||||||
|
return self
|
||||||
if not isinstance(arg, (dict, SimpleNamespace)):
|
if not isinstance(arg, (dict, SimpleNamespace)):
|
||||||
raise ValueError("Only dict or Expando are allowed as argument")
|
raise ValueError("Only dict or Expando are allowed as argument")
|
||||||
properties |= vars(arg) if isinstance(arg, SimpleNamespace) else arg
|
properties |= vars(arg) if isinstance(arg, SimpleNamespace) else arg
|
||||||
|
|||||||
@@ -49,8 +49,14 @@ def get():
|
|||||||
mk.manage_binding(datalist, Binding(data))
|
mk.manage_binding(datalist, Binding(data))
|
||||||
mk.manage_binding(label_elt, Binding(data))
|
mk.manage_binding(label_elt, Binding(data))
|
||||||
|
|
||||||
add_button = mk.button("Add", command=Command("Add", "Add a suggestion", add_suggestion).bind(data))
|
add_button = mk.button("Add", command=Command("Add",
|
||||||
remove_button = mk.button("Remove", command=Command("Remove", "Remove a suggestion", remove_suggestion).bind(data))
|
"Add a suggestion",
|
||||||
|
None,
|
||||||
|
add_suggestion).bind(data))
|
||||||
|
remove_button = mk.button("Remove", command=Command("Remove",
|
||||||
|
"Remove a suggestion",
|
||||||
|
None,
|
||||||
|
remove_suggestion).bind(data))
|
||||||
|
|
||||||
return Div(
|
return Div(
|
||||||
add_button,
|
add_button,
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ def say_hello():
|
|||||||
|
|
||||||
|
|
||||||
# Create the command
|
# Create the command
|
||||||
hello_command = Command("say_hello", "Responds with a greeting", say_hello)
|
hello_command = Command("say_hello",
|
||||||
|
"Responds with a greeting",
|
||||||
|
None,
|
||||||
|
say_hello)
|
||||||
|
|
||||||
# Create the app
|
# Create the app
|
||||||
app, rt = create_app(protect_routes=False)
|
app, rt = create_app(protect_routes=False)
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ def change_text():
|
|||||||
return "New text"
|
return "New text"
|
||||||
|
|
||||||
|
|
||||||
command = Command("change_text", "change the text", change_text).htmx(target="#text")
|
command = Command("change_text",
|
||||||
|
"change the text",
|
||||||
|
None,
|
||||||
|
change_text).htmx(target="#text")
|
||||||
|
|
||||||
|
|
||||||
@rt("/")
|
@rt("/")
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ def test_i_can_mk_button_with_attrs():
|
|||||||
def test_i_can_mk_button_with_command(user, rt):
|
def test_i_can_mk_button_with_command(user, rt):
|
||||||
def new_value(value): return value
|
def new_value(value): return value
|
||||||
|
|
||||||
command = Command('test', 'TestingCommand', new_value, "this is my new value")
|
command = Command('test', 'TestingCommand', None, new_value, "this is my new value")
|
||||||
|
|
||||||
@rt('/')
|
@rt('/')
|
||||||
def get(): return mk.button('button', command)
|
def get(): return mk.button('button', command)
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ class TestTabsManagerBehaviour:
|
|||||||
assert tab_id in tabs_manager.get_state().tabs
|
assert tab_id in tabs_manager.get_state().tabs
|
||||||
assert tabs_manager.get_state().tabs[tab_id]["label"] == "Tab1"
|
assert tabs_manager.get_state().tabs[tab_id]["label"] == "Tab1"
|
||||||
assert tabs_manager.get_state().tabs[tab_id]["id"] == tab_id
|
assert tabs_manager.get_state().tabs[tab_id]["id"] == tab_id
|
||||||
assert tabs_manager.get_state().tabs[tab_id]["component_type"] is None
|
assert tabs_manager.get_state().tabs[tab_id]["component"] is None
|
||||||
assert tabs_manager.get_state().tabs[tab_id]["component_id"] is None
|
assert tabs_manager.get_state().tabs[tab_id]["component_parent"] is None
|
||||||
assert tabs_manager.get_state().tabs_order == [tab_id]
|
assert tabs_manager.get_state().tabs_order == [tab_id]
|
||||||
assert tabs_manager.get_state().active_tab == tab_id
|
assert tabs_manager.get_state().active_tab == tab_id
|
||||||
|
|
||||||
@@ -61,9 +61,12 @@ class TestTabsManagerBehaviour:
|
|||||||
vis_network = VisNetwork(tabs_manager, nodes=[], edges=[])
|
vis_network = VisNetwork(tabs_manager, nodes=[], edges=[])
|
||||||
tab_id = tabs_manager.create_tab("Network", vis_network)
|
tab_id = tabs_manager.create_tab("Network", vis_network)
|
||||||
|
|
||||||
|
component_type, component_id = vis_network.get_prefix(), vis_network.get_id()
|
||||||
|
parent_type, parent_id = tabs_manager.get_prefix(), tabs_manager.get_id()
|
||||||
|
|
||||||
assert tab_id is not None
|
assert tab_id is not None
|
||||||
assert tabs_manager.get_state().tabs[tab_id]["component_type"] == vis_network.get_prefix()
|
assert tabs_manager.get_state().tabs[tab_id]["component"] == (component_type, component_id)
|
||||||
assert tabs_manager.get_state().tabs[tab_id]["component_id"] == vis_network.get_id()
|
assert tabs_manager.get_state().tabs[tab_id]["component_parent"] == (parent_type, parent_id)
|
||||||
|
|
||||||
def test_i_can_create_multiple_tabs(self, tabs_manager):
|
def test_i_can_create_multiple_tabs(self, tabs_manager):
|
||||||
"""Test creating multiple tabs maintains correct order and activation."""
|
"""Test creating multiple tabs maintains correct order and activation."""
|
||||||
|
|||||||
@@ -27,21 +27,21 @@ def reset_command_manager():
|
|||||||
class TestCommandDefault:
|
class TestCommandDefault:
|
||||||
|
|
||||||
def test_i_can_create_a_command_with_no_params(self):
|
def test_i_can_create_a_command_with_no_params(self):
|
||||||
command = Command('test', 'Command description', callback)
|
command = Command('test', 'Command description', None, callback)
|
||||||
assert command.id is not None
|
assert command.id is not None
|
||||||
assert command.name == 'test'
|
assert command.name == 'test'
|
||||||
assert command.description == 'Command description'
|
assert command.description == 'Command description'
|
||||||
assert command.execute() == "Hello World"
|
assert command.execute() == "Hello World"
|
||||||
|
|
||||||
def test_command_are_registered(self):
|
def test_command_are_registered(self):
|
||||||
command = Command('test', 'Command description', callback)
|
command = Command('test', 'Command description', None, callback)
|
||||||
assert CommandsManager.commands.get(str(command.id)) is command
|
assert CommandsManager.commands.get(str(command.id)) is command
|
||||||
|
|
||||||
|
|
||||||
class TestCommandBind:
|
class TestCommandBind:
|
||||||
|
|
||||||
def test_i_can_bind_a_command_to_an_element(self):
|
def test_i_can_bind_a_command_to_an_element(self):
|
||||||
command = Command('test', 'Command description', callback)
|
command = Command('test', 'Command description', None, callback)
|
||||||
elt = Button()
|
elt = Button()
|
||||||
updated = command.bind_ft(elt)
|
updated = command.bind_ft(elt)
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ class TestCommandBind:
|
|||||||
assert matches(updated, expected)
|
assert matches(updated, expected)
|
||||||
|
|
||||||
def test_i_can_suppress_swapping_with_target_attr(self):
|
def test_i_can_suppress_swapping_with_target_attr(self):
|
||||||
command = Command('test', 'Command description', callback).htmx(target=None)
|
command = Command('test', 'Command description', None, callback).htmx(target=None)
|
||||||
elt = Button()
|
elt = Button()
|
||||||
updated = command.bind_ft(elt)
|
updated = command.bind_ft(elt)
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ class TestCommandBind:
|
|||||||
|
|
||||||
make_observable(data)
|
make_observable(data)
|
||||||
bind(data, "value", on_data_change)
|
bind(data, "value", on_data_change)
|
||||||
command = Command('test', 'Command description', another_callback).bind(data)
|
command = Command('test', 'Command description', None, another_callback).bind(data)
|
||||||
|
|
||||||
res = command.execute()
|
res = command.execute()
|
||||||
|
|
||||||
@@ -88,14 +88,14 @@ class TestCommandBind:
|
|||||||
|
|
||||||
make_observable(data)
|
make_observable(data)
|
||||||
bind(data, "value", on_data_change)
|
bind(data, "value", on_data_change)
|
||||||
command = Command('test', 'Command description', another_callback).bind(data)
|
command = Command('test', 'Command description', None, another_callback).bind(data)
|
||||||
|
|
||||||
res = command.execute()
|
res = command.execute()
|
||||||
|
|
||||||
assert res == ["another 1", "another 2", ("hello", "new value")]
|
assert res == ["another 1", "another 2", ("hello", "new value")]
|
||||||
|
|
||||||
def test_by_default_swap_is_set_to_outer_html(self):
|
def test_by_default_swap_is_set_to_outer_html(self):
|
||||||
command = Command('test', 'Command description', callback)
|
command = Command('test', 'Command description', None, callback)
|
||||||
elt = Button()
|
elt = Button()
|
||||||
updated = command.bind_ft(elt)
|
updated = command.bind_ft(elt)
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ class TestCommandBind:
|
|||||||
def another_callback():
|
def another_callback():
|
||||||
return return_values
|
return return_values
|
||||||
|
|
||||||
command = Command('test', 'Command description', another_callback)
|
command = Command('test', 'Command description', None, another_callback)
|
||||||
|
|
||||||
res = command.execute()
|
res = command.execute()
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ class TestCommandBind:
|
|||||||
class TestCommandExecute:
|
class TestCommandExecute:
|
||||||
|
|
||||||
def test_i_can_create_a_command_with_no_params(self):
|
def test_i_can_create_a_command_with_no_params(self):
|
||||||
command = Command('test', 'Command description', callback)
|
command = Command('test', 'Command description', None, callback)
|
||||||
assert command.id is not None
|
assert command.id is not None
|
||||||
assert command.name == 'test'
|
assert command.name == 'test'
|
||||||
assert command.description == 'Command description'
|
assert command.description == 'Command description'
|
||||||
@@ -137,7 +137,7 @@ class TestCommandExecute:
|
|||||||
def callback_with_param(param):
|
def callback_with_param(param):
|
||||||
return f"Hello {param}"
|
return f"Hello {param}"
|
||||||
|
|
||||||
command = Command('test', 'Command description', callback_with_param, "world")
|
command = Command('test', 'Command description', None, callback_with_param, "world")
|
||||||
assert command.execute() == "Hello world"
|
assert command.execute() == "Hello world"
|
||||||
|
|
||||||
def test_i_can_execute_a_command_with_open_parameter(self):
|
def test_i_can_execute_a_command_with_open_parameter(self):
|
||||||
@@ -146,7 +146,7 @@ class TestCommandExecute:
|
|||||||
def callback_with_param(name):
|
def callback_with_param(name):
|
||||||
return f"Hello {name}"
|
return f"Hello {name}"
|
||||||
|
|
||||||
command = Command('test', 'Command description', callback_with_param)
|
command = Command('test', 'Command description', None, callback_with_param)
|
||||||
assert command.execute(client_response={"name": "world"}) == "Hello world"
|
assert command.execute(client_response={"name": "world"}) == "Hello world"
|
||||||
|
|
||||||
def test_i_can_convert_arg_in_execute(self):
|
def test_i_can_convert_arg_in_execute(self):
|
||||||
@@ -155,7 +155,7 @@ class TestCommandExecute:
|
|||||||
def callback_with_param(number: int):
|
def callback_with_param(number: int):
|
||||||
assert isinstance(number, int)
|
assert isinstance(number, int)
|
||||||
|
|
||||||
command = Command('test', 'Command description', callback_with_param)
|
command = Command('test', 'Command description', None, callback_with_param)
|
||||||
command.execute(client_response={"number": "10"})
|
command.execute(client_response={"number": "10"})
|
||||||
|
|
||||||
def test_swap_oob_is_added_when_multiple_elements_are_returned(self):
|
def test_swap_oob_is_added_when_multiple_elements_are_returned(self):
|
||||||
@@ -164,7 +164,7 @@ class TestCommandExecute:
|
|||||||
def another_callback():
|
def another_callback():
|
||||||
return Div(id="first"), Div(id="second"), "hello", Div(id="third")
|
return Div(id="first"), Div(id="second"), "hello", Div(id="third")
|
||||||
|
|
||||||
command = Command('test', 'Command description', another_callback)
|
command = Command('test', 'Command description', None, another_callback)
|
||||||
|
|
||||||
res = command.execute()
|
res = command.execute()
|
||||||
assert "hx-swap-oob" not in res[0].attrs
|
assert "hx-swap-oob" not in res[0].attrs
|
||||||
@@ -177,7 +177,7 @@ class TestCommandExecute:
|
|||||||
def another_callback():
|
def another_callback():
|
||||||
return Div(id="first"), Div(), "hello", Div()
|
return Div(id="first"), Div(), "hello", Div()
|
||||||
|
|
||||||
command = Command('test', 'Command description', another_callback)
|
command = Command('test', 'Command description', None, another_callback)
|
||||||
|
|
||||||
res = command.execute()
|
res = command.execute()
|
||||||
assert "hx-swap-oob" not in res[0].attrs
|
assert "hx-swap-oob" not in res[0].attrs
|
||||||
@@ -188,9 +188,9 @@ class TestCommandExecute:
|
|||||||
class TestLambaCommand:
|
class TestLambaCommand:
|
||||||
|
|
||||||
def test_i_can_create_a_command_from_lambda(self):
|
def test_i_can_create_a_command_from_lambda(self):
|
||||||
command = LambdaCommand(lambda resp: "Hello World")
|
command = LambdaCommand(None, lambda resp: "Hello World")
|
||||||
assert command.execute() == "Hello World"
|
assert command.execute() == "Hello World"
|
||||||
|
|
||||||
def test_by_default_target_is_none(self):
|
def test_by_default_target_is_none(self):
|
||||||
command = LambdaCommand(lambda resp: "Hello World")
|
command = LambdaCommand(None, lambda resp: "Hello World")
|
||||||
assert command.get_htmx_params()["hx-swap"] == "none"
|
assert command.get_htmx_params()["hx-swap"] == "none"
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ def rt(user):
|
|||||||
|
|
||||||
class TestingCommand:
|
class TestingCommand:
|
||||||
def test_i_can_trigger_a_command(self, user):
|
def test_i_can_trigger_a_command(self, user):
|
||||||
command = Command('test', 'TestingCommand', new_value, "this is my new value")
|
command = Command('test', 'TestingCommand', None, new_value, "this is my new value")
|
||||||
testable = TestableElement(user, mk.button('button', command))
|
testable = TestableElement(user, mk.button('button', command))
|
||||||
testable.click()
|
testable.click()
|
||||||
assert user.get_content() == "this is my new value"
|
assert user.get_content() == "this is my new value"
|
||||||
|
|
||||||
def test_error_is_raised_when_command_is_not_found(self, user):
|
def test_error_is_raised_when_command_is_not_found(self, user):
|
||||||
command = Command('test', 'TestingCommand', new_value, "this is my new value")
|
command = Command('test', 'TestingCommand', None, new_value, "this is my new value")
|
||||||
CommandsManager.reset()
|
CommandsManager.reset()
|
||||||
testable = TestableElement(user, mk.button('button', command))
|
testable = TestableElement(user, mk.button('button', command))
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ class TestingCommand:
|
|||||||
assert "not found." in str(exc_info.value)
|
assert "not found." in str(exc_info.value)
|
||||||
|
|
||||||
def test_i_can_play_a_complex_scenario(self, user, rt):
|
def test_i_can_play_a_complex_scenario(self, user, rt):
|
||||||
command = Command('test', 'TestingCommand', new_value, "this is my new value")
|
command = Command('test', 'TestingCommand', None, new_value, "this is my new value")
|
||||||
|
|
||||||
@rt('/')
|
@rt('/')
|
||||||
def get(): return mk.button('button', command)
|
def get(): return mk.button('button', command)
|
||||||
|
|||||||
@@ -463,7 +463,7 @@ class TestPredicates:
|
|||||||
div = Div(hx_post="/url")
|
div = Div(hx_post="/url")
|
||||||
assert HasHtmx(hx_post="/url").validate(div)
|
assert HasHtmx(hx_post="/url").validate(div)
|
||||||
|
|
||||||
c = Command("c", "testing has_htmx", None)
|
c = Command("c", "testing has_htmx", None, None)
|
||||||
c.bind_ft(div)
|
c.bind_ft(div)
|
||||||
assert HasHtmx(command=c).validate(div)
|
assert HasHtmx(command=c).validate(div)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user