I can click on the grid to select a cell
This commit is contained in:
601
docs/DataGrid.md
Normal file
601
docs/DataGrid.md
Normal file
@@ -0,0 +1,601 @@
|
||||
# DataGrid Component
|
||||
|
||||
## Introduction
|
||||
|
||||
The DataGrid component provides a high-performance tabular data display for your FastHTML application. It renders pandas
|
||||
DataFrames with interactive features like column resizing, reordering, and filtering, all powered by HTMX for seamless
|
||||
updates without page reloads.
|
||||
|
||||
**Key features:**
|
||||
|
||||
- Display tabular data from pandas DataFrames
|
||||
- Resizable columns with drag handles
|
||||
- Draggable columns for reordering
|
||||
- Real-time filtering with search bar
|
||||
- Virtual scrolling for large datasets (pagination with lazy loading)
|
||||
- Custom scrollbars for consistent cross-browser appearance
|
||||
- Optional state persistence per session
|
||||
|
||||
**Common use cases:**
|
||||
|
||||
- Data exploration and analysis dashboards
|
||||
- Admin interfaces with tabular data
|
||||
- Report viewers
|
||||
- Database table browsers
|
||||
- CSV/Excel file viewers
|
||||
|
||||
## Quick Start
|
||||
|
||||
Here's a minimal example showing a data table with a pandas DataFrame:
|
||||
|
||||
```python
|
||||
import pandas as pd
|
||||
from fasthtml.common import *
|
||||
from myfasthtml.controls.DataGrid import DataGrid
|
||||
from myfasthtml.core.instances import RootInstance
|
||||
|
||||
# Create sample data
|
||||
df = pd.DataFrame({
|
||||
"Name": ["Alice", "Bob", "Charlie", "Diana"],
|
||||
"Age": [25, 30, 35, 28],
|
||||
"City": ["Paris", "London", "Berlin", "Madrid"]
|
||||
})
|
||||
|
||||
# Create root instance and data grid
|
||||
root = RootInstance(session)
|
||||
grid = DataGrid(parent=root)
|
||||
grid.init_from_dataframe(df)
|
||||
|
||||
# Render the grid
|
||||
return grid
|
||||
```
|
||||
|
||||
This creates a complete data grid with:
|
||||
|
||||
- A header row with column names ("Name", "Age", "City")
|
||||
- Data rows displaying the DataFrame content
|
||||
- A search bar for filtering data
|
||||
- Resizable column borders (drag to resize)
|
||||
- Draggable columns (drag headers to reorder)
|
||||
- Custom scrollbars for horizontal and vertical scrolling
|
||||
|
||||
**Note:** The DataGrid automatically detects column types (Text, Number, Bool, Datetime) from the DataFrame dtypes and
|
||||
applies appropriate formatting.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Visual Structure
|
||||
|
||||
The DataGrid component consists of a filter bar, a table with header/body/footer, and custom scrollbars:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ Filter Bar │
|
||||
│ ┌─────────────────────────────────────────────┐ ┌────┐ │
|
||||
│ │ 🔍 Search... │ │ ✕ │ │
|
||||
│ └─────────────────────────────────────────────┘ └────┘ │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ Header Row ▲ │
|
||||
│ ┌──────────┬──────────┬──────────┬──────────┐ │ │
|
||||
│ │ Column 1 │ Column 2 │ Column 3 │ Column 4 │ █ │
|
||||
│ └──────────┴──────────┴──────────┴──────────┘ █ │
|
||||
├────────────────────────────────────────────────────────█───┤
|
||||
│ Body (scrollable) █ │
|
||||
│ ┌──────────┬──────────┬──────────┬──────────┐ █ │
|
||||
│ │ Value │ Value │ Value │ Value │ █ │
|
||||
│ ├──────────┼──────────┼──────────┼──────────┤ │ │
|
||||
│ │ Value │ Value │ Value │ Value │ │ │
|
||||
│ ├──────────┼──────────┼──────────┼──────────┤ ▼ │
|
||||
│ │ Value │ Value │ Value │ Value │ │
|
||||
│ └──────────┴──────────┴──────────┴──────────┘ │
|
||||
│ ◄═══════════════════════════════════════════════════════► │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Component details:**
|
||||
|
||||
| Element | Description |
|
||||
|------------|-------------------------------------------------------|
|
||||
| Filter bar | Search input with filter mode toggle and clear button |
|
||||
| Header row | Column names with resize handles and drag support |
|
||||
| Body | Scrollable data rows with virtual pagination |
|
||||
| Scrollbars | Custom vertical and horizontal scrollbars |
|
||||
|
||||
### Creating a DataGrid
|
||||
|
||||
The DataGrid is a `MultipleInstance`, meaning you can create multiple independent grids in your application. Create it
|
||||
by providing a parent instance:
|
||||
|
||||
```python
|
||||
grid = DataGrid(parent=root_instance)
|
||||
|
||||
# Or with a custom ID
|
||||
grid = DataGrid(parent=root_instance, _id="my-grid")
|
||||
|
||||
# Or with state persistence enabled
|
||||
grid = DataGrid(parent=root_instance, save_state=True)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `parent`: Parent instance (required)
|
||||
- `_id` (str, optional): Custom identifier for the grid
|
||||
- `save_state` (bool, optional): Enable state persistence (column widths, order, filters)
|
||||
|
||||
### Loading Data
|
||||
|
||||
Use the `init_from_dataframe()` method to load data into the grid:
|
||||
|
||||
```python
|
||||
import pandas as pd
|
||||
|
||||
# Create a DataFrame
|
||||
df = pd.DataFrame({
|
||||
"Product": ["Laptop", "Phone", "Tablet"],
|
||||
"Price": [999.99, 699.99, 449.99],
|
||||
"In Stock": [True, False, True]
|
||||
})
|
||||
|
||||
# Load into grid
|
||||
grid.init_from_dataframe(df)
|
||||
```
|
||||
|
||||
**Column type detection:**
|
||||
|
||||
The DataGrid automatically detects column types from pandas dtypes:
|
||||
|
||||
| pandas dtype | DataGrid type | Display |
|
||||
|--------------------|---------------|-------------------------|
|
||||
| `int64`, `float64` | Number | Right-aligned |
|
||||
| `bool` | Bool | Checkbox icon |
|
||||
| `datetime64` | Datetime | Formatted date |
|
||||
| `object`, others | Text | Left-aligned, truncated |
|
||||
|
||||
### Row Index Column
|
||||
|
||||
By default, the DataGrid displays a row index column on the left. This can be useful for identifying rows:
|
||||
|
||||
```python
|
||||
# Row index is enabled by default
|
||||
grid._state.row_index = True
|
||||
|
||||
# To disable the row index column
|
||||
grid._state.row_index = False
|
||||
grid.init_from_dataframe(df)
|
||||
```
|
||||
|
||||
## Column Features
|
||||
|
||||
### Resizing Columns
|
||||
|
||||
Users can resize columns by dragging the border between column headers:
|
||||
|
||||
- **Drag handle location**: Right edge of each column header
|
||||
- **Minimum width**: 30 pixels
|
||||
- **Persistence**: Resized widths are automatically saved when `save_state=True`
|
||||
|
||||
The resize interaction:
|
||||
|
||||
1. Hover over the right edge of a column header (cursor changes)
|
||||
2. Click and drag to resize
|
||||
3. Release to confirm the new width
|
||||
4. Double-click to reset to default width
|
||||
|
||||
**Programmatic width control:**
|
||||
|
||||
```python
|
||||
# Set a specific column width
|
||||
for col in grid._state.columns:
|
||||
if col.col_id == "my_column":
|
||||
col.width = 200 # pixels
|
||||
break
|
||||
```
|
||||
|
||||
### Moving Columns
|
||||
|
||||
Users can reorder columns by dragging column headers:
|
||||
|
||||
1. Click and hold a column header
|
||||
2. Drag to the desired position
|
||||
3. Release to drop the column
|
||||
|
||||
The columns animate smoothly during the move, and other columns shift to accommodate the new position.
|
||||
|
||||
**Note:** Column order is persisted when `save_state=True`.
|
||||
|
||||
### Column Visibility
|
||||
|
||||
Columns can be hidden programmatically:
|
||||
|
||||
```python
|
||||
# Hide a specific column
|
||||
for col in grid._state.columns:
|
||||
if col.col_id == "internal_id":
|
||||
col.visible = False
|
||||
break
|
||||
```
|
||||
|
||||
Hidden columns are not rendered but remain in the state, allowing them to be shown again later.
|
||||
|
||||
## Filtering
|
||||
|
||||
### Using the Search Bar
|
||||
|
||||
The DataGrid includes a built-in search bar that filters rows in real-time:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐ ┌────┐
|
||||
│ 🔍 Search... │ │ ✕ │
|
||||
└─────────────────────────────────────────────┘ └────┘
|
||||
│ │
|
||||
│ └── Clear button
|
||||
└── Filter mode icon (click to cycle)
|
||||
```
|
||||
|
||||
**How filtering works:**
|
||||
|
||||
1. Type in the search box
|
||||
2. The grid filters rows where ANY visible column contains the search text
|
||||
3. Matching text is highlighted in the results
|
||||
4. Click the ✕ button to clear the filter
|
||||
|
||||
### Filter Modes
|
||||
|
||||
Click the filter icon to cycle through three modes:
|
||||
|
||||
| Mode | Icon | Description |
|
||||
|------------|------|------------------------------------|
|
||||
| **Filter** | 🔍 | Hides non-matching rows |
|
||||
| **Search** | 🔎 | Highlights matches, shows all rows |
|
||||
| **AI** | 🧠 | AI-powered search (future feature) |
|
||||
|
||||
The current mode affects how results are displayed:
|
||||
|
||||
- **Filter mode**: Only matching rows are shown
|
||||
- **Search mode**: All rows shown, matches highlighted
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### State Persistence
|
||||
|
||||
Enable state persistence to save user preferences across sessions:
|
||||
|
||||
```python
|
||||
# Enable state persistence
|
||||
grid = DataGrid(parent=root, save_state=True)
|
||||
```
|
||||
|
||||
**What gets persisted:**
|
||||
|
||||
| State | Description |
|
||||
|-------------------|---------------------------------|
|
||||
| Column widths | User-resized column sizes |
|
||||
| Column order | User-defined column arrangement |
|
||||
| Column visibility | Which columns are shown/hidden |
|
||||
| Sort order | Current sort configuration |
|
||||
| Filter state | Active filters |
|
||||
|
||||
### Virtual Scrolling
|
||||
|
||||
For large datasets, the DataGrid uses virtual scrolling with lazy loading:
|
||||
|
||||
- Only a subset of rows (page) is rendered initially
|
||||
- As the user scrolls down, more rows are loaded automatically
|
||||
- Uses Intersection Observer API for efficient scroll detection
|
||||
- Default page size: configurable via `DATAGRID_PAGE_SIZE`
|
||||
|
||||
This allows smooth performance even with thousands of rows.
|
||||
|
||||
### Text Size
|
||||
|
||||
Customize the text size for the grid body:
|
||||
|
||||
```python
|
||||
# Available sizes: "xs", "sm", "md", "lg"
|
||||
grid._settings.text_size = "sm" # default
|
||||
```
|
||||
|
||||
### CSS Customization
|
||||
|
||||
The DataGrid uses CSS classes that you can customize:
|
||||
|
||||
| Class | Element |
|
||||
|-----------------------------|-------------------------|
|
||||
| `dt2-table-wrapper` | Root table container |
|
||||
| `dt2-table` | Table element |
|
||||
| `dt2-header-container` | Header wrapper |
|
||||
| `dt2-body-container` | Scrollable body wrapper |
|
||||
| `dt2-footer-container` | Footer wrapper |
|
||||
| `dt2-row` | Table row |
|
||||
| `dt2-cell` | Table cell |
|
||||
| `dt2-resize-handle` | Column resize handle |
|
||||
| `dt2-scrollbars-vertical` | Vertical scrollbar |
|
||||
| `dt2-scrollbars-horizontal` | Horizontal scrollbar |
|
||||
| `dt2-highlight-1` | Search match highlight |
|
||||
|
||||
**Example customization:**
|
||||
|
||||
```css
|
||||
/* Change highlight color */
|
||||
.dt2-highlight-1 {
|
||||
background-color: #fef08a;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Customize row hover */
|
||||
.dt2-row:hover {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
/* Style the scrollbars */
|
||||
.dt2-scrollbars-vertical,
|
||||
.dt2-scrollbars-horizontal {
|
||||
background-color: #3b82f6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Simple Data Table
|
||||
|
||||
A basic data table displaying product information:
|
||||
|
||||
```python
|
||||
import pandas as pd
|
||||
from fasthtml.common import *
|
||||
from myfasthtml.controls.DataGrid import DataGrid
|
||||
from myfasthtml.core.instances import RootInstance
|
||||
|
||||
# Sample product data
|
||||
df = pd.DataFrame({
|
||||
"Product": ["Laptop Pro", "Wireless Mouse", "USB-C Hub", "Monitor 27\"", "Keyboard"],
|
||||
"Category": ["Computers", "Accessories", "Accessories", "Displays", "Accessories"],
|
||||
"Price": [1299.99, 49.99, 79.99, 399.99, 129.99],
|
||||
"In Stock": [True, True, False, True, True],
|
||||
"Rating": [4.5, 4.2, 4.8, 4.6, 4.3]
|
||||
})
|
||||
|
||||
# Create and configure grid
|
||||
root = RootInstance(session)
|
||||
grid = DataGrid(parent=root, _id="products-grid")
|
||||
grid.init_from_dataframe(df)
|
||||
|
||||
# Render
|
||||
return Div(
|
||||
H1("Product Catalog"),
|
||||
grid,
|
||||
cls="p-4"
|
||||
)
|
||||
```
|
||||
|
||||
### Example 2: Large Dataset with Filtering
|
||||
|
||||
Handling a large dataset with virtual scrolling and filtering:
|
||||
|
||||
```python
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from fasthtml.common import *
|
||||
from myfasthtml.controls.DataGrid import DataGrid
|
||||
from myfasthtml.core.instances import RootInstance
|
||||
|
||||
# Generate large dataset (10,000 rows)
|
||||
np.random.seed(42)
|
||||
n_rows = 10000
|
||||
|
||||
df = pd.DataFrame({
|
||||
"ID": range(1, n_rows + 1),
|
||||
"Name": [f"Item_{i}" for i in range(n_rows)],
|
||||
"Value": np.random.uniform(10, 1000, n_rows).round(2),
|
||||
"Category": np.random.choice(["A", "B", "C", "D"], n_rows),
|
||||
"Active": np.random.choice([True, False], n_rows),
|
||||
"Created": pd.date_range("2024-01-01", periods=n_rows, freq="h")
|
||||
})
|
||||
|
||||
# Create grid with state persistence
|
||||
root = RootInstance(session)
|
||||
grid = DataGrid(parent=root, _id="large-dataset", save_state=True)
|
||||
grid.init_from_dataframe(df)
|
||||
|
||||
return Div(
|
||||
H1("Large Dataset Explorer"),
|
||||
P(f"Displaying {n_rows:,} rows with virtual scrolling"),
|
||||
grid,
|
||||
cls="p-4",
|
||||
style="height: 100vh;"
|
||||
)
|
||||
```
|
||||
|
||||
**Note:** Virtual scrolling loads rows on demand as you scroll, ensuring smooth performance even with 10,000+ rows.
|
||||
|
||||
### Example 3: Dashboard with Multiple Grids
|
||||
|
||||
An application with multiple data grids in different tabs:
|
||||
|
||||
```python
|
||||
import pandas as pd
|
||||
from fasthtml.common import *
|
||||
from myfasthtml.controls.DataGrid import DataGrid
|
||||
from myfasthtml.controls.TabsManager import TabsManager
|
||||
from myfasthtml.core.instances import RootInstance
|
||||
|
||||
# Create data for different views
|
||||
sales_df = pd.DataFrame({
|
||||
"Date": pd.date_range("2024-01-01", periods=30, freq="D"),
|
||||
"Revenue": [1000 + i * 50 for i in range(30)],
|
||||
"Orders": [10 + i for i in range(30)]
|
||||
})
|
||||
|
||||
customers_df = pd.DataFrame({
|
||||
"Customer": ["Acme Corp", "Tech Inc", "Global Ltd"],
|
||||
"Country": ["USA", "UK", "Germany"],
|
||||
"Total Spent": [15000, 12000, 8500]
|
||||
})
|
||||
|
||||
# Create instances
|
||||
root = RootInstance(session)
|
||||
tabs = TabsManager(parent=root, _id="dashboard-tabs")
|
||||
|
||||
# Create grids
|
||||
sales_grid = DataGrid(parent=root, _id="sales-grid")
|
||||
sales_grid.init_from_dataframe(sales_df)
|
||||
|
||||
customers_grid = DataGrid(parent=root, _id="customers-grid")
|
||||
customers_grid.init_from_dataframe(customers_df)
|
||||
|
||||
# Add to tabs
|
||||
tabs.create_tab("Sales", sales_grid)
|
||||
tabs.create_tab("Customers", customers_grid)
|
||||
|
||||
return Div(
|
||||
H1("Sales Dashboard"),
|
||||
tabs,
|
||||
cls="p-4"
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Developer Reference
|
||||
|
||||
This section contains technical details for developers working on the DataGrid component itself.
|
||||
|
||||
### State
|
||||
|
||||
The DataGrid uses two state objects:
|
||||
|
||||
**DatagridState** - Main state for grid data and configuration:
|
||||
|
||||
| Name | Type | Description | Default |
|
||||
|-------------------|---------------------------|----------------------------|---------|
|
||||
| `sidebar_visible` | bool | Whether sidebar is visible | `False` |
|
||||
| `row_index` | bool | Show row index column | `True` |
|
||||
| `columns` | list[DataGridColumnState] | Column definitions | `[]` |
|
||||
| `rows` | list[DataGridRowState] | Row-specific states | `[]` |
|
||||
| `sorted` | list | Sort configuration | `[]` |
|
||||
| `filtered` | dict | Active filters | `{}` |
|
||||
| `selection` | DatagridSelectionState | Selection state | - |
|
||||
| `ne_df` | DataFrame | The data (non-persisted) | `None` |
|
||||
|
||||
**DatagridSettings** - User preferences:
|
||||
|
||||
| Name | Type | Description | Default |
|
||||
|----------------------|------|--------------------|---------|
|
||||
| `save_state` | bool | Enable persistence | `False` |
|
||||
| `header_visible` | bool | Show header row | `True` |
|
||||
| `filter_all_visible` | bool | Show filter bar | `True` |
|
||||
| `text_size` | str | Body text size | `"sm"` |
|
||||
|
||||
### Column State
|
||||
|
||||
Each column is represented by `DataGridColumnState`:
|
||||
|
||||
| Name | Type | Description | Default |
|
||||
|-------------|------------|--------------------|---------|
|
||||
| `col_id` | str | Column identifier | - |
|
||||
| `col_index` | int | Index in DataFrame | - |
|
||||
| `title` | str | Display title | `None` |
|
||||
| `type` | ColumnType | Data type | `Text` |
|
||||
| `visible` | bool | Is column visible | `True` |
|
||||
| `usable` | bool | Is column usable | `True` |
|
||||
| `width` | int | Width in pixels | `150` |
|
||||
|
||||
### Commands
|
||||
|
||||
Available commands for programmatic control:
|
||||
|
||||
| Name | Description |
|
||||
|------------------------|---------------------------------------------|
|
||||
| `get_page(page_index)` | Load a specific page of data (lazy loading) |
|
||||
| `set_column_width()` | Update column width after resize |
|
||||
| `move_column()` | Move column to new position |
|
||||
| `filter()` | Apply current filter to grid |
|
||||
|
||||
### Public Methods
|
||||
|
||||
| Method | Description |
|
||||
|-----------------------------------------------|----------------------------------------|
|
||||
| `init_from_dataframe(df, init_state=True)` | Load data from pandas DataFrame |
|
||||
| `set_column_width(col_id, width)` | Set column width programmatically |
|
||||
| `move_column(source_col_id, target_col_id)` | Move column to new position |
|
||||
| `filter()` | Apply filter and return partial render |
|
||||
| `render()` | Render the complete grid |
|
||||
| `render_partial(fragment, redraw_scrollbars)` | Render only part of the grid |
|
||||
|
||||
### High Level Hierarchical Structure
|
||||
|
||||
```
|
||||
Div(id="{id}", cls="grid")
|
||||
├── Div (filter bar)
|
||||
│ └── DataGridQuery # Filter/search component
|
||||
├── Div(id="tw_{id}", cls="dt2-table-wrapper")
|
||||
│ ├── Div(id="t_{id}", cls="dt2-table")
|
||||
│ │ ├── Div (dt2-header-container)
|
||||
│ │ │ └── Div(id="th_{id}", cls="dt2-row dt2-header")
|
||||
│ │ │ ├── Div (dt2-cell) # Column 1 header
|
||||
│ │ │ ├── Div (dt2-cell) # Column 2 header
|
||||
│ │ │ └── ...
|
||||
│ │ ├── Div(id="tb_{id}", cls="dt2-body-container")
|
||||
│ │ │ └── Div (dt2-body)
|
||||
│ │ │ ├── Div (dt2-row) # Data row 1
|
||||
│ │ │ ├── Div (dt2-row) # Data row 2
|
||||
│ │ │ └── ...
|
||||
│ │ └── Div (dt2-footer-container)
|
||||
│ │ └── Div (dt2-row dt2-header) # Footer row
|
||||
│ └── Div (dt2-scrollbars)
|
||||
│ ├── Div (dt2-scrollbars-vertical-wrapper)
|
||||
│ │ └── Div (dt2-scrollbars-vertical)
|
||||
│ └── Div (dt2-scrollbars-horizontal-wrapper)
|
||||
│ └── Div (dt2-scrollbars-horizontal)
|
||||
└── Script # Initialization script
|
||||
```
|
||||
|
||||
### Element IDs
|
||||
|
||||
| Pattern | Description |
|
||||
|-----------------------|-------------------------------------|
|
||||
| `{id}` | Root grid container |
|
||||
| `tw_{id}` | Table wrapper (scrollbar container) |
|
||||
| `t_{id}` | Table element |
|
||||
| `th_{id}` | Header row |
|
||||
| `tb_{id}` | Body container |
|
||||
| `tf_{id}` | Footer row |
|
||||
| `tsm_{id}` | Selection Manager |
|
||||
| `tr_{id}-{row_index}` | Individual data row |
|
||||
|
||||
### Internal Methods
|
||||
|
||||
These methods are used internally for rendering:
|
||||
|
||||
| Method | Description |
|
||||
|---------------------------------------------|----------------------------------------|
|
||||
| `mk_headers()` | Renders the header row |
|
||||
| `mk_body()` | Renders the body with first page |
|
||||
| `mk_body_container()` | Renders the scrollable body container |
|
||||
| `mk_body_content_page(page_index)` | Renders a specific page of rows |
|
||||
| `mk_body_cell(col_pos, row_index, col_def)` | Renders a single cell |
|
||||
| `mk_body_cell_content(...)` | Renders cell content with highlighting |
|
||||
| `mk_footers()` | Renders the footer row |
|
||||
| `mk_table()` | Renders the complete table structure |
|
||||
| `mk_aggregation_cell(...)` | Renders footer aggregation cell |
|
||||
| `_get_filtered_df()` | Returns filtered and sorted DataFrame |
|
||||
| `_apply_sort(df)` | Applies sort configuration |
|
||||
| `_apply_filter(df)` | Applies filter configuration |
|
||||
|
||||
### DataGridQuery Component
|
||||
|
||||
The filter bar is a separate component (`DataGridQuery`) with its own state:
|
||||
|
||||
| State Property | Type | Description | Default |
|
||||
|----------------|------|-----------------------------------------|------------|
|
||||
| `filter_type` | str | Current mode ("filter", "search", "ai") | `"filter"` |
|
||||
| `query` | str | Current search text | `None` |
|
||||
|
||||
**Commands:**
|
||||
|
||||
| Command | Description |
|
||||
|------------------------|-----------------------------|
|
||||
| `change_filter_type()` | Cycle through filter modes |
|
||||
| `on_filter_changed()` | Handle search input changes |
|
||||
| `on_cancel_query()` | Clear the search query |
|
||||
Reference in New Issue
Block a user