diff --git a/src/myfasthtml/assets/myfasthtml.css b/src/myfasthtml/assets/myfasthtml.css
index cd09e25..44dba99 100644
--- a/src/myfasthtml/assets/myfasthtml.css
+++ b/src/myfasthtml/assets/myfasthtml.css
@@ -838,14 +838,13 @@
.dt2-footer {
background-color: var(--color-base-200);
border-radius: 10px 10px 0 0;
- min-width: max-content; /* Content width propagates to scrollable parent */
+ min-width: max-content; /* Content width propagates to scrollable parent */
}
/* Body */
.dt2-body {
overflow: hidden;
- font-size: 14px;
- min-width: max-content; /* Content width propagates to scrollable parent */
+ min-width: max-content; /* Content width propagates to scrollable parent */
}
/* Row */
@@ -951,13 +950,17 @@
/* Table with Grid layout - horizontal scroll enabled, scrollbars hidden */
.dt2-table {
+ --color-border: color-mix(in oklab, var(--color-base-content) 20%, #0000);
+ --color-resize: color-mix(in oklab, var(--color-base-content) 50%, #0000);
height: 100%;
display: grid;
- grid-template-rows: auto 1fr auto; /* header, body, footer */
- overflow-x: auto; /* Enable horizontal scroll */
- overflow-y: hidden; /* No vertical scroll on table */
- scrollbar-width: none; /* Firefox: hide scrollbar */
- -ms-overflow-style: none; /* IE/Edge: hide scrollbar */
+ grid-template-rows: auto 1fr auto; /* header, body, footer */
+ overflow-x: auto; /* Enable horizontal scroll */
+ overflow-y: hidden; /* No vertical scroll on table */
+ scrollbar-width: none; /* Firefox: hide scrollbar */
+ -ms-overflow-style: none; /* IE/Edge: hide scrollbar */
+ border: 1px solid var(--color-border);
+ border-radius: 10px;
}
/* Chrome/Safari: hide scrollbar */
@@ -968,20 +971,20 @@
/* Header - no scroll, takes natural height */
.dt2-header-container {
overflow: hidden;
- min-width: max-content; /* Force table to be as wide as content */
+ min-width: max-content; /* Force table to be as wide as content */
}
/* Body - scrollable vertically via JS, scrollbars hidden */
.dt2-body-container {
- overflow: hidden; /* Scrollbars hidden, scroll via JS */
- min-height: 0; /* Important for Grid to allow shrinking */
- min-width: max-content; /* Force table to be as wide as content */
+ overflow: hidden; /* Scrollbars hidden, scroll via JS */
+ min-height: 0; /* Important for Grid to allow shrinking */
+ min-width: max-content; /* Force table to be as wide as content */
}
/* Footer - no scroll, takes natural height */
.dt2-footer-container {
overflow: hidden;
- min-width: max-content; /* Force table to be as wide as content */
+ min-width: max-content; /* Force table to be as wide as content */
}
/* Custom scrollbars container - overlaid on table */
@@ -991,7 +994,7 @@
bottom: 0;
left: 0;
right: 0;
- pointer-events: none; /* Let clicks pass through */
+ pointer-events: none; /* Let clicks pass through */
z-index: 10;
}
@@ -1002,7 +1005,7 @@
background-color: var(--color-base-200);
opacity: 1;
transition: opacity 0.2s ease-in-out;
- pointer-events: auto; /* Enable interaction */
+ pointer-events: auto; /* Enable interaction */
}
/* Vertical scrollbar wrapper - right side, full table height */
@@ -1016,7 +1019,7 @@
/* Horizontal scrollbar wrapper - bottom, full width minus vertical scrollbar */
.dt2-scrollbars-horizontal-wrapper {
left: 0;
- right: 8px; /* Leave space for vertical scrollbar */
+ right: 8px; /* Leave space for vertical scrollbar */
bottom: 0;
height: 8px;
}
diff --git a/src/myfasthtml/controls/DataGrid.py b/src/myfasthtml/controls/DataGrid.py
index 621ecf9..2458269 100644
--- a/src/myfasthtml/controls/DataGrid.py
+++ b/src/myfasthtml/controls/DataGrid.py
@@ -72,6 +72,7 @@ class DatagridSettings(DbObject):
self.views_visible: bool = True
self.open_file_visible: bool = True
self.open_settings_visible: bool = True
+ self.text_size: str = "sm"
class Commands(BaseCommands):
@@ -255,21 +256,21 @@ class DataGrid(MultipleInstance):
if not filter_keyword_lower:
# OPTIMIZATION: Return plain HTML string instead of Label object
# Include "truncate text-sm" to match mk.label() behavior (ellipsis + font size)
- return NotStr(f'{value_str}')
+ return NotStr(f'{value_str}')
index = value_str.lower().find(filter_keyword_lower)
if index < 0:
- return NotStr(f'{value_str}')
+ return NotStr(f'{value_str}')
# Has highlighting - need to use Span objects
# Add "truncate text-sm" to match mk.label() behavior
len_keyword = len(filter_keyword_lower)
res = []
if index > 0:
- res.append(Span(value_str[:index], cls=f"{css_class} text-sm"))
- res.append(Span(value_str[index:index + len_keyword], cls=f"{css_class} text-sm dt2-highlight-1"))
+ res.append(Span(value_str[:index], cls=f"{css_class}"))
+ res.append(Span(value_str[index:index + len_keyword], cls=f"{css_class} dt2-highlight-1"))
if index + len_keyword < len(value_str):
- res.append(Span(value_str[index + len_keyword:], cls=f"{css_class} text-sm"))
+ res.append(Span(value_str[index + len_keyword:], cls=f"{css_class}"))
return Span(*res, cls=f"{css_class} truncate") if len(res) > 1 else res[0]
column_type = col_def.type
@@ -281,7 +282,7 @@ class DataGrid(MultipleInstance):
# RowIndex - simplest case, just return the number as plain HTML
if column_type == ColumnType.RowIndex:
- return NotStr(f'{row_index}')
+ return NotStr(f'{row_index}')
# Convert value to string
value_str = str(value)
@@ -346,7 +347,7 @@ class DataGrid(MultipleInstance):
def mk_body(self):
return Div(
*self.mk_body_content_page(0),
- cls="dt2-body"
+ cls=f"dt2-body text-{self._settings.text_size}",
)
def mk_footers(self):
@@ -469,7 +470,9 @@ class DataGrid(MultipleInstance):
if self._state.ne_df is None:
return Div("No data to display !")
+ from myfasthtml.controls.DataGridFilter import DataGridFilter
return Div(
+ Div(DataGridFilter(self), cls="mb-2"),
self.mk_table(),
Script(f"initDataGrid('{self._id}');"),
id=self._id,
diff --git a/src/myfasthtml/controls/DataGridFilter.py b/src/myfasthtml/controls/DataGridFilter.py
new file mode 100644
index 0000000..85312bb
--- /dev/null
+++ b/src/myfasthtml/controls/DataGridFilter.py
@@ -0,0 +1,76 @@
+import logging
+
+from fasthtml.components import *
+
+from myfasthtml.controls.BaseCommands import BaseCommands
+from myfasthtml.controls.helpers import mk
+from myfasthtml.core.commands import Command
+from myfasthtml.core.dbmanager import DbObject
+from myfasthtml.core.instances import MultipleInstance
+from myfasthtml.icons.fluent import brain_circuit20_regular
+from myfasthtml.icons.fluent_p1 import filter20_regular, search20_regular
+from myfasthtml.icons.fluent_p2 import dismiss_circle20_regular
+
+logger = logging.getLogger("DataGridFilter")
+
+filter_type = {
+ "filter": filter20_regular,
+ "search": search20_regular,
+ "ai": brain_circuit20_regular
+}
+
+
+class DataGridFilterState(DbObject):
+ def __init__(self, owner):
+ with self.initializing():
+ super().__init__(owner)
+ self.filter_type: str = "filter"
+
+
+class Commands(BaseCommands):
+ def change_filter_type(self):
+ return Command("ChangeFilterType",
+ "Change filter type",
+ self._owner,
+ self._owner.change_filter_type).htmx(target=f"#{self._id}")
+
+ def on_filter_changed(self):
+ return Command("FilterChanged",
+ "Filter changed",
+ self._owner,
+ self._owner.filter_changed).htmx(target=None)
+
+
+class DataGridFilter(MultipleInstance):
+ def __init__(self, parent, _id=None):
+ super().__init__(parent, _id=_id or "-filter")
+ self.commands = Commands(self)
+ self._state = DataGridFilterState(self)
+
+ def change_filter_type(self):
+ keys = list(filter_type.keys()) # ["filter", "search", "ai"]
+ current_idx = keys.index(self._state.filter_type)
+ self._state.filter_type = keys[(current_idx + 1) % len(keys)]
+ return self
+
+ def filter_changed(self, f):
+ logger.debug(f"filter_changed {f=}")
+ return self
+
+ def render(self):
+ return Div(
+ mk.label(
+ Input(name="f",
+ placeholder="Search...",
+ **self.commands.on_filter_changed().get_htmx_params(escaped=True)),
+ icon=mk.icon(filter_type[self._state.filter_type], command=self.commands.change_filter_type()),
+ cls="input input-sm flex gap-2"
+ ),
+ mk.icon(dismiss_circle20_regular, size=24),
+ # Keyboard(self, _id="-keyboard").add("enter", self.commands.on_filter_changed()),
+ cls="flex",
+ id=self._id
+ )
+
+ def __ft__(self):
+ return self.render()