I can resize Datagrid columns
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import html
|
||||
import logging
|
||||
import re
|
||||
from functools import lru_cache
|
||||
from typing import Optional
|
||||
@@ -23,6 +24,8 @@ from myfasthtml.icons.fluent_p2 import checkbox_checked16_regular
|
||||
# OPTIMIZATION: Pre-compiled regex to detect HTML special characters
|
||||
_HTML_SPECIAL_CHARS_REGEX = re.compile(r'[<>&"\']')
|
||||
|
||||
logger = logging.getLogger("Datagrid")
|
||||
|
||||
|
||||
@lru_cache(maxsize=2)
|
||||
def _mk_bool_cached(_value):
|
||||
@@ -38,8 +41,8 @@ def _mk_bool_cached(_value):
|
||||
|
||||
class DatagridState(DbObject):
|
||||
def __init__(self, owner, save_state):
|
||||
super().__init__(owner, name=f"{owner.get_full_id()}-state", save_state=save_state)
|
||||
with self.initializing():
|
||||
super().__init__(owner, name=f"{owner.get_full_id()}#state", save_state=save_state)
|
||||
self.sidebar_visible: bool = False
|
||||
self.selected_view: str = None
|
||||
self.row_index: bool = True
|
||||
@@ -59,8 +62,9 @@ class DatagridState(DbObject):
|
||||
|
||||
class DatagridSettings(DbObject):
|
||||
def __init__(self, owner, save_state):
|
||||
super().__init__(owner, name=f"{owner.get_full_id()}-settings", save_state=save_state)
|
||||
with self.initializing():
|
||||
super().__init__(owner, name=f"{owner.get_full_id()}#settings", save_state=save_state)
|
||||
self.save_state = save_state is True
|
||||
self.file_name: Optional[str] = None
|
||||
self.selected_sheet_name: Optional[str] = None
|
||||
self.header_visible: bool = True
|
||||
@@ -82,21 +86,28 @@ class Commands(BaseCommands):
|
||||
trigger=f"intersect root:#tb_{self._id} once",
|
||||
auto_swap_oob=False
|
||||
)
|
||||
|
||||
def set_column_width(self):
|
||||
return Command("SetColumnWidth",
|
||||
"Set column width after resize",
|
||||
self._owner,
|
||||
self._owner.set_column_width
|
||||
).htmx(target=None)
|
||||
|
||||
|
||||
class DataGrid(MultipleInstance):
|
||||
def __init__(self, parent, settings=None, save_state=False, _id=None):
|
||||
def __init__(self, parent, settings=None, save_state=None, _id=None):
|
||||
super().__init__(parent, _id=_id)
|
||||
self._settings = settings or DatagridSettings(self, save_state=save_state)
|
||||
self._state = DatagridState(self, save_state=save_state)
|
||||
self._state = DatagridState(self, save_state=self._settings.save_state)
|
||||
self.commands = Commands(self)
|
||||
self.init_from_dataframe(self._state.ne_df)
|
||||
self.init_from_dataframe(self._state.ne_df, init_state=False) # state comes from DatagridState
|
||||
|
||||
@property
|
||||
def _df(self):
|
||||
return self._state.ne_df
|
||||
|
||||
def init_from_dataframe(self, df):
|
||||
def init_from_dataframe(self, df, init_state=True):
|
||||
|
||||
def _get_column_type(dtype):
|
||||
if pd.api.types.is_integer_dtype(dtype):
|
||||
@@ -146,15 +157,28 @@ class DataGrid(MultipleInstance):
|
||||
|
||||
if df is not None:
|
||||
self._state.ne_df = df
|
||||
self._df.columns = self._df.columns.map(make_safe_id) # make sure column names are trimmed
|
||||
self._state.rows = [DataGridRowState(row_id) for row_id in self._df.index]
|
||||
self._state.columns = _init_columns(df) # use df not self._df to keep the original title
|
||||
if init_state:
|
||||
self._df.columns = self._df.columns.map(make_safe_id) # make sure column names are trimmed
|
||||
self._state.rows = [DataGridRowState(row_id) for row_id in self._df.index]
|
||||
self._state.columns = _init_columns(df) # use df not self._df to keep the original title
|
||||
self._state.ns_fast_access = _init_fast_access(self._df)
|
||||
self._state.ns_total_rows = len(self._df) if self._df is not None else 0
|
||||
|
||||
return self
|
||||
|
||||
def set_column_width(self, col_id: str, width: str):
|
||||
"""Update column width after resize. Called via Command from JS."""
|
||||
logger.debug(f"set_column_width: {col_id=} {width=}")
|
||||
for col in self._state.columns:
|
||||
if col.col_id == col_id:
|
||||
col.width = int(width)
|
||||
break
|
||||
|
||||
self._state.save()
|
||||
|
||||
def mk_headers(self):
|
||||
resize_cmd = self.commands.set_column_width()
|
||||
|
||||
def _mk_header_name(col_def: DataGridColumnState):
|
||||
return Div(
|
||||
mk.label(col_def.title, name="dt2-header-title"),
|
||||
@@ -164,7 +188,7 @@ class DataGrid(MultipleInstance):
|
||||
def _mk_header(col_def: DataGridColumnState):
|
||||
return Div(
|
||||
_mk_header_name(col_def),
|
||||
Div(cls="dt2-resize-handle"),
|
||||
Div(cls="dt2-resize-handle", data_command_id=resize_cmd.id),
|
||||
style=f"width:{col_def.width}px;",
|
||||
data_col=col_def.col_id,
|
||||
data_tooltip=col_def.title,
|
||||
@@ -403,10 +427,10 @@ class DataGrid(MultipleInstance):
|
||||
def render(self):
|
||||
if self._state.ne_df is None:
|
||||
return Div("No data to display !")
|
||||
|
||||
|
||||
return Div(
|
||||
self.mk_table(),
|
||||
Script(f"initDataGridScrollbars('{self._id}');"),
|
||||
Script(f"initDataGrid('{self._id}');"),
|
||||
id=self._id,
|
||||
style="height: 100%;"
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ class DocumentDefinition:
|
||||
document_id: str
|
||||
namespace: str
|
||||
name: str
|
||||
type: str # table, card,
|
||||
type: str # table, card,
|
||||
tab_id: str
|
||||
datagrid_id: str
|
||||
|
||||
@@ -92,7 +92,7 @@ class DataGridsManager(MultipleInstance):
|
||||
def open_from_excel(self, tab_id, file_upload: FileUpload):
|
||||
excel_content = file_upload.get_content()
|
||||
df = pd.read_excel(BytesIO(excel_content), file_upload.get_sheet_name())
|
||||
dg = DataGrid(self._tabs_manager, save_state=True)
|
||||
dg = DataGrid(self._tabs_manager, save_state=True) # first time the Datagrid is created
|
||||
dg.init_from_dataframe(df)
|
||||
document = DocumentDefinition(
|
||||
document_id=str(uuid.uuid4()),
|
||||
@@ -112,12 +112,12 @@ class DataGridsManager(MultipleInstance):
|
||||
document_id = self._tree.get_bag(node_id)
|
||||
try:
|
||||
document = next(filter(lambda x: x.document_id == document_id, self._state.elements))
|
||||
dg = DataGrid(self._tabs_manager, _id=document.datagrid_id)
|
||||
dg = DataGrid(self._tabs_manager, _id=document.datagrid_id) # reload the state & settings
|
||||
return self._tabs_manager.show_or_create_tab(document.tab_id, document.name, dg)
|
||||
except StopIteration:
|
||||
# the selected node is not a document (it's a folder)
|
||||
return None
|
||||
|
||||
|
||||
def create_tab_content(self, tab_id):
|
||||
"""
|
||||
Recreate the content for a tab managed by this DataGridsManager.
|
||||
@@ -131,16 +131,16 @@ class DataGridsManager(MultipleInstance):
|
||||
"""
|
||||
# Find the document associated with this tab
|
||||
document = next((d for d in self._state.elements if d.tab_id == tab_id), None)
|
||||
|
||||
|
||||
if document is None:
|
||||
raise ValueError(f"No document found for tab {tab_id}")
|
||||
|
||||
|
||||
# Recreate the DataGrid with its saved state
|
||||
dg = DataGrid(self._tabs_manager, _id=document.datagrid_id)
|
||||
|
||||
dg = DataGrid(self._tabs_manager, _id=document.datagrid_id) # reload the state & settings
|
||||
|
||||
# Wrap in Panel
|
||||
return Panel(self).set_main(dg)
|
||||
|
||||
|
||||
def clear_tree(self):
|
||||
self._state.elements = []
|
||||
self._tree.clear()
|
||||
|
||||
@@ -129,7 +129,7 @@ class TabsManager(MultipleInstance):
|
||||
# 2. Get or create parent
|
||||
if tab_config["component_parent"] is None:
|
||||
logger.error(f"No parent defined for tab {tab_id}")
|
||||
return Div("Failed to retrieve tab content.")
|
||||
return Div("Failed to retrieve tab content (no parent).")
|
||||
|
||||
parent = InstancesManager.get(self._session, tab_config["component_parent"][1], None)
|
||||
if parent is None:
|
||||
@@ -146,11 +146,11 @@ class TabsManager(MultipleInstance):
|
||||
return content
|
||||
except Exception as e:
|
||||
logger.error(f"Error while parent creating tab content: {e}")
|
||||
return Div("Failed to retrieve tab content.")
|
||||
return Div("Failed to retrieve tab content (cannot create).")
|
||||
else:
|
||||
# Parent doesn't support create_tab_content, fallback to error
|
||||
logger.error(f"Parent {tab_config['component_parent'][1]} doesn't support create_tab_content")
|
||||
return Div("Failed to retrieve tab content.")
|
||||
return Div("Failed to retrieve tab content (create tab not supported).")
|
||||
|
||||
def _get_or_create_tab_content(self, tab_id):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user