Working on Datagrid interaction

This commit is contained in:
2025-12-08 22:39:26 +01:00
parent 045f01b48a
commit b26abc4257
6 changed files with 36 additions and 17 deletions

View File

@@ -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):

View File

@@ -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
@@ -76,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(),
@@ -89,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 = []

View File

@@ -111,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.")
@@ -384,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
@@ -399,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

View File

@@ -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):

View File

@@ -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

View File

@@ -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."""