diff --git a/src/myfasthtml/controls/TabsManager.py b/src/myfasthtml/controls/TabsManager.py
index eafe18f..8915575 100644
--- a/src/myfasthtml/controls/TabsManager.py
+++ b/src/myfasthtml/controls/TabsManager.py
@@ -114,12 +114,12 @@ class TabsManager(MultipleInstance):
logger.debug(f"on_new_tab {label=}, {component=}, {auto_increment=}")
if auto_increment:
label = f"{label}_{self._get_tab_count()}"
- tab_id = self.add_tab(label, component)
component = component or VisNetwork(self._session, nodes=vis_nodes, edges=vis_edges)
+ tab_id = self.add_tab(label, component)
return (
self._mk_tabs_controller(),
self._wrap_tab_content(self._mk_tab_content(tab_id, component)),
- self._mk_tabs_header(True),
+ self._mk_tabs_header_wrapper(True),
)
def add_tab(self, label: str, component: Any, activate: bool = True) -> str:
@@ -258,7 +258,7 @@ class TabsManager(MultipleInstance):
Script(f'updateTabs("{self._id}-controller");'),
)
- def _mk_tabs_header(self, oob=False):
+ def _mk_tabs_header_wrapper(self, oob=False):
# Create visible tab buttons
visible_tab_buttons = [
self._mk_tab_button(self._state.tabs[tab_id])
@@ -318,14 +318,20 @@ class TabsManager(MultipleInstance):
Returns:
Div element containing the active tab content or empty container
"""
- content = None
- if self._state.active_tab and self._state.active_tab in self._state._tabs_content:
- component = self._state._tabs_content[self._state.active_tab]
- content = component
+ if self._state.active_tab:
+ active_tab = self._state.active_tab
+ if active_tab in self._state._tabs_content:
+ tab_content = self._state._tabs_content[active_tab]
+ else:
+ content = self._get_tab_content(active_tab)
+ tab_content = self._mk_tab_content(active_tab, content)
+ self._state._tabs_content[active_tab] = tab_content
+ else:
+ tab_content = self._mk_tab_content("", None)
return Div(
- self._mk_tab_content(self._state.active_tab, content),
+ tab_content,
cls="mf-tab-content-wrapper",
id=f"{self._id}-content-wrapper",
)
@@ -374,7 +380,7 @@ class TabsManager(MultipleInstance):
"""
return Div(
self._mk_tabs_controller(),
- self._mk_tabs_header(),
+ self._mk_tabs_header_wrapper(),
self._mk_tab_content_wrapper(),
cls="mf-tabs-manager",
id=self._id,
diff --git a/src/myfasthtml/controls/VisNetwork.py b/src/myfasthtml/controls/VisNetwork.py
index 3e41069..9da5a91 100644
--- a/src/myfasthtml/controls/VisNetwork.py
+++ b/src/myfasthtml/controls/VisNetwork.py
@@ -3,48 +3,72 @@ import logging
from fasthtml.components import Script, Div
from myfasthtml.controls.helpers import Ids
+from myfasthtml.core.dbmanager import DbObject
from myfasthtml.core.instances import MultipleInstance
logger = logging.getLogger("VisNetwork")
+
+class VisNetworkState(DbObject):
+ def __init__(self, owner):
+ super().__init__(owner.get_session(), owner.get_id())
+ with self.initializing():
+ # persisted in DB
+ self.nodes: list = []
+ self.edges: list = []
+ self.options: dict = {
+ "autoResize": True,
+ "interaction": {
+ "dragNodes": True,
+ "zoomView": True,
+ "dragView": True,
+ },
+ "physics": {"enabled": True}
+ }
+
+
class VisNetwork(MultipleInstance):
def __init__(self, session, _id=None, nodes=None, edges=None, options=None):
super().__init__(session, Ids.VisNetwork, _id=_id)
logger.debug(f"VisNetwork created with id: {self._id}")
- # Default values
- self.nodes = nodes or []
- self.edges = edges or []
- self.options = options or {
- "autoResize": True,
- "interaction": {
- "dragNodes": True,
- "zoomView": True,
- "dragView": True,
- },
- "physics": {"enabled": True}
- }
+ self._state = VisNetworkState(self)
+ self._update_state(nodes, edges, options)
+
+ def _update_state(self, nodes, edges, options):
+ logger.debug(f"Updating VisNetwork state with {nodes=}, {edges=}, {options=}")
+ if not nodes and not edges and not options:
+ return
+
+ state = self._state.copy()
+ if nodes is not None:
+ state.nodes = nodes
+ if edges is not None:
+ state.edges = edges
+ if options is not None:
+ state.options = options
+
+ self._state.update(state)
def render(self):
# Prepare JS arrays (no JSON library needed)
js_nodes = ",\n ".join(
f'{{ id: {n["id"]}, label: "{n.get("label", "")}" }}'
- for n in self.nodes
+ for n in self._state.nodes
)
js_edges = ",\n ".join(
f'{{ from: {e["from"]}, to: {e["to"]} }}'
- for e in self.edges
+ for e in self._state.edges
)
# Convert Python options to JS
import json
- js_options = json.dumps(self.options, indent=2)
+ js_options = json.dumps(self._state.options, indent=2)
return (
Div(
id=self._id,
cls="mf-vis",
- #style="width:100%; height:100%;", # Let parent control the layout
),
# The script initializing Vis.js
diff --git a/src/myfasthtml/core/instances.py b/src/myfasthtml/core/instances.py
index 094bda3..9fa3e3a 100644
--- a/src/myfasthtml/core/instances.py
+++ b/src/myfasthtml/core/instances.py
@@ -60,7 +60,7 @@ class MultipleInstance(BaseInstance):
"""
def __init__(self, session: dict, prefix: str, auto_register: bool = True, _id=None):
- super().__init__(session, prefix, f"{prefix}-{_id or str(uuid.uuid4())}", auto_register)
+ super().__init__(session, prefix, _id or f"{prefix}-{str(uuid.uuid4())}", auto_register)
self._prefix = prefix