working on improving component loading
This commit is contained in:
@@ -12,6 +12,8 @@ from myfasthtml.core.constants import ColumnType, ROW_INDEX_ID, FooterAggregatio
|
|||||||
from myfasthtml.core.dbmanager import DbObject
|
from myfasthtml.core.dbmanager import DbObject
|
||||||
from myfasthtml.core.instances import MultipleInstance
|
from myfasthtml.core.instances import MultipleInstance
|
||||||
from myfasthtml.core.utils import make_safe_id
|
from myfasthtml.core.utils import make_safe_id
|
||||||
|
from myfasthtml.icons.fluent import checkbox_unchecked16_regular
|
||||||
|
from myfasthtml.icons.fluent_p2 import checkbox_checked16_regular
|
||||||
|
|
||||||
|
|
||||||
class DatagridState(DbObject):
|
class DatagridState(DbObject):
|
||||||
@@ -149,7 +151,7 @@ class DataGrid(MultipleInstance):
|
|||||||
def mk_body_cell_content(self, col_pos, row_index, col_def: DataGridColumnState):
|
def mk_body_cell_content(self, col_pos, row_index, col_def: DataGridColumnState):
|
||||||
|
|
||||||
def mk_bool(_value):
|
def mk_bool(_value):
|
||||||
return Div(mk.icon(icon_checked if _value else icon_unchecked, can_select=False),
|
return Div(mk.icon(checkbox_checked16_regular if _value else checkbox_unchecked16_regular, can_select=False),
|
||||||
cls="dt2-cell-content-checkbox")
|
cls="dt2-cell-content-checkbox")
|
||||||
|
|
||||||
def mk_text(_value):
|
def mk_text(_value):
|
||||||
@@ -301,7 +303,7 @@ class DataGrid(MultipleInstance):
|
|||||||
else:
|
else:
|
||||||
value = None
|
value = None
|
||||||
|
|
||||||
return Div(mk_ellipsis(value, cls="dt2-cell-content-number"),
|
return Div(mk.label(value, cls="dt2-cell-content-number"),
|
||||||
data_col=col_def.col_id,
|
data_col=col_def.col_id,
|
||||||
style=f"width:{col_def.width}px;",
|
style=f"width:{col_def.width}px;",
|
||||||
cls="dt2-cell dt2-footer-cell",
|
cls="dt2-cell dt2-footer-cell",
|
||||||
|
|||||||
@@ -115,11 +115,18 @@ class TabsManager(MultipleInstance):
|
|||||||
def _dynamic_get_content(self, tab_id):
|
def _dynamic_get_content(self, tab_id):
|
||||||
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"] is None:
|
if tab_config["component"] is None:
|
||||||
return Div("Tab content does not support serialization.")
|
return Div("Tab content does not support serialization.")
|
||||||
|
|
||||||
|
res = InstancesManager.get(self._session, tab_config["component"][1], None)
|
||||||
|
if res is not None:
|
||||||
|
return res
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return InstancesManager.get(self._session, tab_config["component"][1])
|
logger.debug(f"Component not created yet. Need to manually create it.")
|
||||||
|
return InstancesManager.dynamic_get(self._session, tab_config["component_parent"], tab_config["component"])
|
||||||
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.")
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import uuid
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from myfasthtml.controls.helpers import Ids
|
from myfasthtml.controls.helpers import Ids
|
||||||
from myfasthtml.core.utils import pascal_to_snake
|
from myfasthtml.core.utils import pascal_to_snake, get_class, snake_to_pascal
|
||||||
|
|
||||||
logger = logging.getLogger("InstancesManager")
|
logger = logging.getLogger("InstancesManager")
|
||||||
|
|
||||||
@@ -183,16 +183,22 @@ class InstancesManager:
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(session: dict, instance_id: str):
|
def get(session: dict, instance_id: str, default="**__no_default__**"):
|
||||||
"""
|
"""
|
||||||
Get or create an instance of the given type (from its id)
|
Get or create an instance of the given type (from its id)
|
||||||
:param session:
|
:param session:
|
||||||
:param instance_id:
|
:param instance_id:
|
||||||
|
:param default:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
session_id = InstancesManager.get_session_id(session)
|
try:
|
||||||
key = (session_id, instance_id)
|
session_id = InstancesManager.get_session_id(session)
|
||||||
return InstancesManager.instances[key]
|
key = (session_id, instance_id)
|
||||||
|
return InstancesManager.instances[key]
|
||||||
|
except KeyError:
|
||||||
|
if default != "**__non__**":
|
||||||
|
return default
|
||||||
|
raise
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_type(session: dict, cls: type):
|
def get_by_type(session: dict, cls: type):
|
||||||
@@ -202,6 +208,19 @@ class InstancesManager:
|
|||||||
assert len(res) > 0, f"No instance of type {cls.__name__} found"
|
assert len(res) > 0, f"No instance of type {cls.__name__} found"
|
||||||
return res[0]
|
return res[0]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def dynamic_get(session, component_parent: tuple, component: tuple):
|
||||||
|
parent_type, parent_id = component_parent
|
||||||
|
component_type, component_id = component
|
||||||
|
|
||||||
|
# parent should always exist
|
||||||
|
parent = InstancesManager.get(session, parent_id)
|
||||||
|
|
||||||
|
real_component_type = snake_to_pascal(component_type.removeprefix("mf-"))
|
||||||
|
component_full_type = f"myfasthtml.controls.{real_component_type}.{real_component_type}"
|
||||||
|
cls = get_class(component_full_type)
|
||||||
|
return cls(parent, _id=component_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_session_id(session):
|
def get_session_id(session):
|
||||||
if isinstance(session, str):
|
if isinstance(session, str):
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@@ -317,6 +318,29 @@ def make_safe_id(s: str | None):
|
|||||||
res = re.sub('-', '_', make_html_id(s)) # replace '-' by '_'
|
res = re.sub('-', '_', make_html_id(s)) # replace '-' by '_'
|
||||||
return res.lower() # no uppercase
|
return res.lower() # no uppercase
|
||||||
|
|
||||||
|
|
||||||
|
def get_class(qualified_class_name: str):
|
||||||
|
"""
|
||||||
|
Dynamically loads and returns a class type from its fully qualified name.
|
||||||
|
Note that the class is not instantiated.
|
||||||
|
|
||||||
|
:param qualified_class_name: Fully qualified name of the class (e.g., 'some.module.ClassName').
|
||||||
|
:return: The class object.
|
||||||
|
:raises ImportError: If the module cannot be imported.
|
||||||
|
:raises AttributeError: If the class cannot be resolved in the module.
|
||||||
|
"""
|
||||||
|
module_name, class_name = qualified_class_name.rsplit(".", 1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
except ModuleNotFoundError as e:
|
||||||
|
raise ImportError(f"Could not import module '{module_name}' for '{qualified_class_name}': {e}")
|
||||||
|
|
||||||
|
if not hasattr(module, class_name):
|
||||||
|
raise AttributeError(f"Component '{class_name}' not found in '{module.__name__}'.")
|
||||||
|
|
||||||
|
return getattr(module, class_name)
|
||||||
|
|
||||||
@utils_rt(Routes.Commands)
|
@utils_rt(Routes.Commands)
|
||||||
def post(session, c_id: str, client_response: dict = None):
|
def post(session, c_id: str, client_response: dict = None):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from myfasthtml.core.utils import flatten, make_html_id
|
from myfasthtml.core.utils import flatten, make_html_id, pascal_to_snake, snake_to_pascal
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("input_args,expected,test_description", [
|
@pytest.mark.parametrize("input_args,expected,test_description", [
|
||||||
@@ -64,3 +64,46 @@ def test_i_can_flatten(input_args, expected, test_description):
|
|||||||
])
|
])
|
||||||
def test_i_can_have_valid_html_id(string, expected):
|
def test_i_can_have_valid_html_id(string, expected):
|
||||||
assert make_html_id(string) == expected
|
assert make_html_id(string) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("input_str, expected, test_description", [
|
||||||
|
("MyClass", "my_class", "simple PascalCase"),
|
||||||
|
("myVariable", "my_variable", "camelCase"),
|
||||||
|
("HTTPServer", "http_server", "short uppercase sequence"),
|
||||||
|
("XMLHttpRequest", "xml_http_request", "long uppercase sequence"),
|
||||||
|
("A", "a", "single letter"),
|
||||||
|
("already_snake", "already_snake", "already snake_case"),
|
||||||
|
("MyClass123", "my_class123", "with numbers"),
|
||||||
|
("MyLongClassName", "my_long_class_name", "long class name"),
|
||||||
|
(" MyClass ", "my_class", "with spaces to trim"),
|
||||||
|
("iPhone", "i_phone", "starts lowercase then uppercase"),
|
||||||
|
(None, None, "None input"),
|
||||||
|
("", "", "empty string"),
|
||||||
|
(" ", "", "only spaces"),
|
||||||
|
])
|
||||||
|
def test_i_can_convert_pascal_to_snake(input_str, expected, test_description):
|
||||||
|
"""Test that pascal_to_snake correctly converts PascalCase/camelCase to snake_case."""
|
||||||
|
result = pascal_to_snake(input_str)
|
||||||
|
assert result == expected, f"Failed for test case: {test_description}"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("input_str, expected, test_description", [
|
||||||
|
("my_class", "MyClass", "simple snake_case"),
|
||||||
|
("my_long_class_name", "MyLongClassName", "long class name"),
|
||||||
|
("a", "A", "single letter"),
|
||||||
|
("myclass", "Myclass", "no underscore"),
|
||||||
|
(" my_class ", "MyClass", "with spaces to trim"),
|
||||||
|
("my__class", "MyClass", "multiple consecutive underscores"),
|
||||||
|
("_my_class", "MyClass", "starts with underscore"),
|
||||||
|
("my_class_", "MyClass", "ends with underscore"),
|
||||||
|
("_my_class_", "MyClass", "starts and ends with underscore"),
|
||||||
|
("my_class_123", "MyClass123", "with numbers"),
|
||||||
|
(None, None, "None input"),
|
||||||
|
("", "", "empty string"),
|
||||||
|
(" ", "", "only spaces"),
|
||||||
|
("___", "", "only underscores"),
|
||||||
|
])
|
||||||
|
def test_i_can_convert_snake_to_pascal(input_str, expected, test_description):
|
||||||
|
"""Test that snake_to_pascal correctly converts snake_case to PascalCase."""
|
||||||
|
result = snake_to_pascal(input_str)
|
||||||
|
assert result == expected, f"Failed for test case: {test_description}"
|
||||||
|
|||||||
Reference in New Issue
Block a user