Added RowIndex in GridState.

Fixed content escaping
This commit is contained in:
2025-08-23 00:29:52 +02:00
parent b48aaf4621
commit f08ae4a90b
4 changed files with 36 additions and 7 deletions

View File

@@ -1,8 +1,10 @@
import copy import copy
import html
import logging import logging
from io import BytesIO from io import BytesIO
from typing import Literal, Any from typing import Literal, Any
import numpy as np
import pandas as pd import pandas as pd
from fasthtml.components import * from fasthtml.components import *
from fasthtml.xtend import Script from fasthtml.xtend import Script
@@ -121,14 +123,21 @@ class DataGrid(BaseComponent):
else: else:
return ColumnType.Text # Default to Text if no match return ColumnType.Text # Default to Text if no match
def _init_columns(_df):
columns = [DataGridColumnState(make_safe_id(col_id),
col_index,
col_id,
_get_column_type(_df[make_safe_id(col_id)].dtype))
for col_index, col_id in enumerate(_df.columns)]
if self._state.row_index:
columns.insert(0, DataGridColumnState(make_safe_id(ROW_INDEX_ID), -1, " ", ColumnType.RowIndex))
return columns
self._df = df.copy() self._df = df.copy()
self._df.columns = self._df.columns.map(make_safe_id) # make sure column names are trimmed 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.rows = [DataGridRowState(row_id) for row_id in self._df.index]
self._state.columns = [DataGridColumnState(make_safe_id(col_id), self._state.columns = _init_columns(self._df)
col_index,
col_id,
_get_column_type(self._df[make_safe_id(col_id)].dtype))
for col_index, col_id in enumerate(df.columns)]
self._fast_access = self._init_fast_access(self._df) self._fast_access = self._init_fast_access(self._df)
self._total_rows = len(self._df) if self._df is not None else 0 self._total_rows = len(self._df) if self._df is not None else 0
@@ -560,7 +569,7 @@ class DataGrid(BaseComponent):
return mk_my_ellipsis(_value, cls="dt2-cell-content-number") return mk_my_ellipsis(_value, cls="dt2-cell-content-number")
def process_cell_content(_value): def process_cell_content(_value):
value_str = str(_value) value_str = html.escape(str(_value))
if FILTER_INPUT_CID not in self._state.filtered or ( if FILTER_INPUT_CID not in self._state.filtered or (
keyword := self._state.filtered[FILTER_INPUT_CID]) is None: keyword := self._state.filtered[FILTER_INPUT_CID]) is None:
@@ -869,7 +878,9 @@ class DataGrid(BaseComponent):
if df is None: if df is None:
return {} return {}
return {col: df[col].to_numpy() for col in df.columns} res = {col: df[col].to_numpy() for col in df.columns}
res[ROW_INDEX_ID] = df.index.to_numpy()
return res
@timed @timed
def __ft__(self): def __ft__(self):

View File

@@ -19,6 +19,8 @@ DATAGRID_STATE_FOOTER = "footer"
DATAGRID_PAGE_SIZE = 50 DATAGRID_PAGE_SIZE = 50
ROW_INDEX_ID = "__row_index__"
class Routes: class Routes:
Filter = "/filter" # request the filtering in the grid Filter = "/filter" # request the filtering in the grid
ResetFilter = "/reset_filter" # ResetFilter = "/reset_filter" #

View File

@@ -69,6 +69,7 @@ class DataGridSettings:
class DataGridState: class DataGridState:
sidebar_visible: bool = False sidebar_visible: bool = False
selected_view: str = None selected_view: str = None
row_index: bool = False
columns: list[DataGridColumnState] = dataclasses.field(default_factory=list) columns: list[DataGridColumnState] = dataclasses.field(default_factory=list)
rows: list[DataGridRowState] = dataclasses.field(default_factory=list) # only the rows that have a specific state rows: list[DataGridRowState] = dataclasses.field(default_factory=list) # only the rows that have a specific state
footers: list[DataGridFooterConf] = dataclasses.field(default_factory=list) footers: list[DataGridFooterConf] = dataclasses.field(default_factory=list)

View File

@@ -509,3 +509,18 @@ def test_i_can_compute_footer_menu_position_when_not_enough_space(dg):
) )
assert matches(menu, expected) assert matches(menu, expected)
def test_the_content_of_the_cell_is_escaped(empty_dg):
df = pd.DataFrame({
'value': ['<div> My Content </div>'],
'value2': ['{My Content}'],
})
my_dg = empty_dg.init_from_dataframe(df)
actual = my_dg.__ft__()
table_content = extract_table_values_new(actual, header=True)
assert table_content == OrderedDict({
'value': ['&lt;div&gt; My Content &lt;/div&gt;'],
'value2': ['{My Content}']})