I can edit a cell
This commit is contained in:
@@ -205,7 +205,7 @@ def render(self):
|
|||||||
return Div(
|
return Div(
|
||||||
self._mk_content(),
|
self._mk_content(),
|
||||||
Keyboard(self, _id="-keyboard").add("esc", self.commands.close()),
|
Keyboard(self, _id="-keyboard").add("esc", self.commands.close()),
|
||||||
Mouse(self, _id="-mouse").add("click", self.commands.on_click()),
|
Mouse(self, _id="-mouse").add("click", self.commands.handle_on_click()),
|
||||||
id=self._id
|
id=self._id
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -209,13 +209,14 @@ For interactive controls, compose `Keyboard` and `Mouse`:
|
|||||||
from myfasthtml.controls.Keyboard import Keyboard
|
from myfasthtml.controls.Keyboard import Keyboard
|
||||||
from myfasthtml.controls.Mouse import Mouse
|
from myfasthtml.controls.Mouse import Mouse
|
||||||
|
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
return Div(
|
return Div(
|
||||||
self._mk_content(),
|
self._mk_content(),
|
||||||
Keyboard(self, _id="-keyboard").add("esc", self.commands.close()),
|
Keyboard(self, _id="-keyboard").add("esc", self.commands.close()),
|
||||||
Mouse(self, _id="-mouse").add("click", self.commands.on_click()),
|
Mouse(self, _id="-mouse").add("click", self.commands.handle_on_click()),
|
||||||
id=self._id
|
id=self._id
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
pendingMatches: [], // Array of matches waiting for timeout
|
pendingMatches: [], // Array of matches waiting for timeout
|
||||||
sequenceTimeout: 500, // 500ms timeout for sequences
|
sequenceTimeout: 500, // 500ms timeout for sequences
|
||||||
clickHandler: null,
|
clickHandler: null,
|
||||||
|
dblclickHandler: null, // Handler reference for dblclick
|
||||||
contextmenuHandler: null,
|
contextmenuHandler: null,
|
||||||
mousedownState: null, // Active drag state (only after movement detected)
|
mousedownState: null, // Active drag state (only after movement detected)
|
||||||
suppressNextClick: false, // Prevents click from firing after mousedown>mouseup
|
suppressNextClick: false, // Prevents click from firing after mousedown>mouseup
|
||||||
@@ -35,7 +36,9 @@
|
|||||||
|
|
||||||
// Handle aliases
|
// Handle aliases
|
||||||
const aliasMap = {
|
const aliasMap = {
|
||||||
'rclick': 'right_click'
|
'rclick': 'right_click',
|
||||||
|
'double_click': 'dblclick',
|
||||||
|
'dclick': 'dblclick'
|
||||||
};
|
};
|
||||||
|
|
||||||
return aliasMap[normalized] || normalized;
|
return aliasMap[normalized] || normalized;
|
||||||
@@ -563,6 +566,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle dblclick events (triggers for all registered elements).
|
||||||
|
* Uses a fresh single-step history so it never conflicts with click sequences.
|
||||||
|
* @param {MouseEvent} event - The dblclick event
|
||||||
|
*/
|
||||||
|
function handleDblClick(event) {
|
||||||
|
const snapshot = createSnapshot(event, 'dblclick');
|
||||||
|
const dblclickHistory = [snapshot];
|
||||||
|
|
||||||
|
const currentMatches = [];
|
||||||
|
|
||||||
|
for (const [elementId, data] of MouseRegistry.elements) {
|
||||||
|
const element = document.getElementById(elementId);
|
||||||
|
if (!element) continue;
|
||||||
|
|
||||||
|
const isInside = element.contains(event.target);
|
||||||
|
const currentNode = traverseTree(data.tree, dblclickHistory);
|
||||||
|
if (!currentNode || !currentNode.config) continue;
|
||||||
|
|
||||||
|
currentMatches.push({
|
||||||
|
elementId: elementId,
|
||||||
|
config: currentNode.config,
|
||||||
|
combinationStr: currentNode.combinationStr,
|
||||||
|
isInside: isInside
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const anyMatchInside = currentMatches.some(m => m.isInside);
|
||||||
|
if (anyMatchInside && !isInInputContext()) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const match of currentMatches) {
|
||||||
|
triggerHtmxAction(match.elementId, match.config, match.combinationStr, match.isInside, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean up mousedown state and safety timeout
|
* Clean up mousedown state and safety timeout
|
||||||
*/
|
*/
|
||||||
@@ -989,11 +1029,13 @@
|
|||||||
if (!MouseRegistry.listenerAttached) {
|
if (!MouseRegistry.listenerAttached) {
|
||||||
// Store handler references for proper removal
|
// Store handler references for proper removal
|
||||||
MouseRegistry.clickHandler = (e) => handleMouseEvent(e, 'click');
|
MouseRegistry.clickHandler = (e) => handleMouseEvent(e, 'click');
|
||||||
|
MouseRegistry.dblclickHandler = (e) => handleDblClick(e);
|
||||||
MouseRegistry.contextmenuHandler = (e) => handleMouseEvent(e, 'right_click');
|
MouseRegistry.contextmenuHandler = (e) => handleMouseEvent(e, 'right_click');
|
||||||
MouseRegistry.mousedownHandler = (e) => handleMouseDown(e);
|
MouseRegistry.mousedownHandler = (e) => handleMouseDown(e);
|
||||||
MouseRegistry.mouseupHandler = (e) => handleMouseUp(e);
|
MouseRegistry.mouseupHandler = (e) => handleMouseUp(e);
|
||||||
|
|
||||||
document.addEventListener('click', MouseRegistry.clickHandler);
|
document.addEventListener('click', MouseRegistry.clickHandler);
|
||||||
|
document.addEventListener('dblclick', MouseRegistry.dblclickHandler);
|
||||||
document.addEventListener('contextmenu', MouseRegistry.contextmenuHandler);
|
document.addEventListener('contextmenu', MouseRegistry.contextmenuHandler);
|
||||||
document.addEventListener('mousedown', MouseRegistry.mousedownHandler);
|
document.addEventListener('mousedown', MouseRegistry.mousedownHandler);
|
||||||
document.addEventListener('mouseup', MouseRegistry.mouseupHandler);
|
document.addEventListener('mouseup', MouseRegistry.mouseupHandler);
|
||||||
@@ -1007,6 +1049,7 @@
|
|||||||
function detachGlobalListener() {
|
function detachGlobalListener() {
|
||||||
if (MouseRegistry.listenerAttached) {
|
if (MouseRegistry.listenerAttached) {
|
||||||
document.removeEventListener('click', MouseRegistry.clickHandler);
|
document.removeEventListener('click', MouseRegistry.clickHandler);
|
||||||
|
document.removeEventListener('dblclick', MouseRegistry.dblclickHandler);
|
||||||
document.removeEventListener('contextmenu', MouseRegistry.contextmenuHandler);
|
document.removeEventListener('contextmenu', MouseRegistry.contextmenuHandler);
|
||||||
document.removeEventListener('mousedown', MouseRegistry.mousedownHandler);
|
document.removeEventListener('mousedown', MouseRegistry.mousedownHandler);
|
||||||
document.removeEventListener('mouseup', MouseRegistry.mouseupHandler);
|
document.removeEventListener('mouseup', MouseRegistry.mouseupHandler);
|
||||||
@@ -1014,6 +1057,7 @@
|
|||||||
|
|
||||||
// Clean up handler references
|
// Clean up handler references
|
||||||
MouseRegistry.clickHandler = null;
|
MouseRegistry.clickHandler = null;
|
||||||
|
MouseRegistry.dblclickHandler = null;
|
||||||
MouseRegistry.contextmenuHandler = null;
|
MouseRegistry.contextmenuHandler = null;
|
||||||
MouseRegistry.mousedownHandler = null;
|
MouseRegistry.mousedownHandler = null;
|
||||||
MouseRegistry.mouseupHandler = null;
|
MouseRegistry.mouseupHandler = null;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ function initDataGrid(gridId) {
|
|||||||
initDataGridMouseOver(gridId);
|
initDataGridMouseOver(gridId);
|
||||||
makeDatagridColumnsResizable(gridId);
|
makeDatagridColumnsResizable(gridId);
|
||||||
makeDatagridColumnsMovable(gridId);
|
makeDatagridColumnsMovable(gridId);
|
||||||
updateDatagridSelection(gridId)
|
updateDatagridSelection(gridId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import html
|
import html
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from dataclasses import dataclass
|
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
@@ -18,9 +17,7 @@ from myfasthtml.controls.Keyboard import Keyboard
|
|||||||
from myfasthtml.controls.Mouse import Mouse
|
from myfasthtml.controls.Mouse import Mouse
|
||||||
from myfasthtml.controls.Panel import Panel, PanelConf
|
from myfasthtml.controls.Panel import Panel, PanelConf
|
||||||
from myfasthtml.controls.Query import Query, QUERY_FILTER
|
from myfasthtml.controls.Query import Query, QUERY_FILTER
|
||||||
from myfasthtml.controls.datagrid_objects import DataGridColumnState, DataGridRowUiState, \
|
from myfasthtml.controls.datagrid_objects import *
|
||||||
DatagridSelectionState, DataGridHeaderFooterConf, DatagridEditionState, DataGridColumnUiState, \
|
|
||||||
DataGridRowSelectionColumnState
|
|
||||||
from myfasthtml.controls.helpers import mk
|
from myfasthtml.controls.helpers import mk
|
||||||
from myfasthtml.core.commands import Command
|
from myfasthtml.core.commands import Command
|
||||||
from myfasthtml.core.constants import ColumnType, FooterAggregation, DATAGRID_PAGE_SIZE, FILTER_INPUT_CID
|
from myfasthtml.core.constants import ColumnType, FooterAggregation, DATAGRID_PAGE_SIZE, FILTER_INPUT_CID
|
||||||
@@ -156,7 +153,7 @@ class Commands(BaseCommands):
|
|||||||
return Command("OnClick",
|
return Command("OnClick",
|
||||||
"Click on the table",
|
"Click on the table",
|
||||||
self._owner,
|
self._owner,
|
||||||
self._owner.on_click
|
self._owner.handle_on_click
|
||||||
).htmx(target=f"#tsm_{self._id}")
|
).htmx(target=f"#tsm_{self._id}")
|
||||||
|
|
||||||
def on_key_pressed(self):
|
def on_key_pressed(self):
|
||||||
@@ -213,6 +210,21 @@ class Commands(BaseCommands):
|
|||||||
self._owner.on_column_changed
|
self._owner.on_column_changed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def start_edition(self):
|
||||||
|
return Command("StartEdition",
|
||||||
|
"Enter cell edit mode",
|
||||||
|
self._owner,
|
||||||
|
self._owner.handle_start_edition
|
||||||
|
).htmx(target=f"#tsm_{self._id}")
|
||||||
|
|
||||||
|
def save_edition(self):
|
||||||
|
return Command("SaveEdition",
|
||||||
|
"Save cell edition",
|
||||||
|
self._owner,
|
||||||
|
self._owner.handle_save_edition
|
||||||
|
).htmx(target=f"#tsm_{self._id}",
|
||||||
|
trigger="blur, keydown[key=='Enter']")
|
||||||
|
|
||||||
|
|
||||||
class DataGrid(MultipleInstance):
|
class DataGrid(MultipleInstance):
|
||||||
def __init__(self, parent, conf=None, save_state=None, _id=None):
|
def __init__(self, parent, conf=None, save_state=None, _id=None):
|
||||||
@@ -293,10 +305,12 @@ class DataGrid(MultipleInstance):
|
|||||||
"click": {"command": self.commands.on_click(), "hx_vals": "js:getCellId()"},
|
"click": {"command": self.commands.on_click(), "hx_vals": "js:getCellId()"},
|
||||||
"ctrl+click": {"command": self.commands.on_click(), "hx_vals": "js:getCellId()"},
|
"ctrl+click": {"command": self.commands.on_click(), "hx_vals": "js:getCellId()"},
|
||||||
"shift+click": {"command": self.commands.on_click(), "hx_vals": "js:getCellId()"},
|
"shift+click": {"command": self.commands.on_click(), "hx_vals": "js:getCellId()"},
|
||||||
|
"dblclick": {"command": self.commands.start_edition(), "hx_vals": "js:getCellId()"},
|
||||||
}
|
}
|
||||||
|
|
||||||
self._key_support = {
|
self._key_support = {
|
||||||
"esc": {"command": self.commands.on_key_pressed(), "require_inside": False},
|
"esc": {"command": self.commands.on_key_pressed(), "require_inside": False},
|
||||||
|
"enter": {"command": self.commands.on_key_pressed(), "require_inside": True},
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(f"DataGrid '{self.get_table_name()}' with id='{self._id}' created.")
|
logger.debug(f"DataGrid '{self.get_table_name()}' with id='{self._id}' created.")
|
||||||
@@ -451,6 +465,26 @@ class DataGrid(MultipleInstance):
|
|||||||
if self._settings.enable_edition:
|
if self._settings.enable_edition:
|
||||||
self._columns.insert(0, DataGridRowSelectionColumnState())
|
self._columns.insert(0, DataGridRowSelectionColumnState())
|
||||||
|
|
||||||
|
def _enter_edition(self, pos):
|
||||||
|
col_pos, row_index = pos
|
||||||
|
col_def = self._columns[col_pos]
|
||||||
|
if col_def.type in (ColumnType.RowSelection_, ColumnType.RowIndex, ColumnType.Formula):
|
||||||
|
return self.render_partial()
|
||||||
|
self._state.edition.under_edition = pos
|
||||||
|
self._state.save()
|
||||||
|
return self.render_partial("cell", pos=pos)
|
||||||
|
|
||||||
|
def _convert_edition_value(self, value_str, col_type):
|
||||||
|
if col_type == ColumnType.Number:
|
||||||
|
try:
|
||||||
|
return float(value_str) if '.' in value_str else int(value_str)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return value_str
|
||||||
|
elif col_type == ColumnType.Bool:
|
||||||
|
return value_str.lower() in ('true', '1', 'yes')
|
||||||
|
else:
|
||||||
|
return value_str
|
||||||
|
|
||||||
def add_new_column(self, col_def: DataGridColumnState) -> None:
|
def add_new_column(self, col_def: DataGridColumnState) -> None:
|
||||||
"""Add a new column, delegating data mutation to DataService.
|
"""Add a new column, delegating data mutation to DataService.
|
||||||
|
|
||||||
@@ -571,14 +605,20 @@ class DataGrid(MultipleInstance):
|
|||||||
self._state.filtered[FILTER_INPUT_CID] = self._datagrid_filter.get_query()
|
self._state.filtered[FILTER_INPUT_CID] = self._datagrid_filter.get_query()
|
||||||
return self.render_partial("body")
|
return self.render_partial("body")
|
||||||
|
|
||||||
def on_click(self, combination, is_inside, cell_id):
|
def handle_on_click(self, combination, is_inside, cell_id):
|
||||||
logger.debug(f"on_click table={self.get_table_name()} {combination=} {is_inside=} {cell_id=}")
|
logger.debug(f"on_click table={self.get_table_name()} {combination=} {is_inside=} {cell_id=}")
|
||||||
if is_inside and cell_id:
|
if is_inside and cell_id:
|
||||||
self._state.selection.extra_selected.clear()
|
self._state.selection.extra_selected.clear()
|
||||||
|
|
||||||
if cell_id.startswith("tcell_"):
|
pos = self._get_pos_from_element_id(cell_id)
|
||||||
pos = self._get_pos_from_element_id(cell_id)
|
|
||||||
self._update_current_position(pos)
|
if (self._settings.enable_edition and
|
||||||
|
pos is not None and
|
||||||
|
pos == self._state.selection.selected and
|
||||||
|
self._state.edition.under_edition is None):
|
||||||
|
return self._enter_edition(pos)
|
||||||
|
|
||||||
|
self._update_current_position(pos)
|
||||||
|
|
||||||
return self.render_partial()
|
return self.render_partial()
|
||||||
|
|
||||||
@@ -605,6 +645,11 @@ class DataGrid(MultipleInstance):
|
|||||||
if combination == "esc":
|
if combination == "esc":
|
||||||
self._update_current_position(None)
|
self._update_current_position(None)
|
||||||
self._state.selection.extra_selected.clear()
|
self._state.selection.extra_selected.clear()
|
||||||
|
elif (combination == "enter" and
|
||||||
|
self._settings.enable_edition and
|
||||||
|
self._state.selection.selected and
|
||||||
|
self._state.edition.under_edition is None):
|
||||||
|
return self._enter_edition(self._state.selection.selected)
|
||||||
|
|
||||||
return self.render_partial()
|
return self.render_partial()
|
||||||
|
|
||||||
@@ -674,6 +719,30 @@ class DataGrid(MultipleInstance):
|
|||||||
self._panel.set_title(side="right", title="Formatting")
|
self._panel.set_title(side="right", title="Formatting")
|
||||||
self._panel.set_right(self._formatting_editor)
|
self._panel.set_right(self._formatting_editor)
|
||||||
|
|
||||||
|
def handle_start_edition(self, cell_id):
|
||||||
|
logger.debug(f"handle_start_edition: {cell_id=}")
|
||||||
|
if not self._settings.enable_edition:
|
||||||
|
return self.render_partial()
|
||||||
|
if self._state.edition.under_edition is not None:
|
||||||
|
return self.render_partial()
|
||||||
|
pos = self._get_pos_from_element_id(cell_id)
|
||||||
|
if pos is None:
|
||||||
|
return self.render_partial()
|
||||||
|
self._update_current_position(pos)
|
||||||
|
return self._enter_edition(pos)
|
||||||
|
|
||||||
|
def handle_save_edition(self, value):
|
||||||
|
logger.debug(f"handle_save_edition: {value=}")
|
||||||
|
if self._state.edition.under_edition is None:
|
||||||
|
return self.render_partial()
|
||||||
|
col_pos, row_index = self._state.edition.under_edition
|
||||||
|
col_def = self._columns[col_pos]
|
||||||
|
typed_value = self._convert_edition_value(value, col_def.type)
|
||||||
|
self._data_service.set_data(col_def.col_id, row_index, typed_value)
|
||||||
|
self._state.edition.under_edition = None
|
||||||
|
self._state.save()
|
||||||
|
return self.render_partial("cell", pos=(col_pos, row_index))
|
||||||
|
|
||||||
def handle_set_column_width(self, col_id: str, width: str):
|
def handle_set_column_width(self, col_id: str, width: str):
|
||||||
"""Update column width after resize. Called via Command from JS."""
|
"""Update column width after resize. Called via Command from JS."""
|
||||||
logger.debug(f"set_column_width: {col_id=} {width=}")
|
logger.debug(f"set_column_width: {col_id=} {width=}")
|
||||||
@@ -724,6 +793,31 @@ class DataGrid(MultipleInstance):
|
|||||||
def get_data_service_id_from_data_grid_id(datagrid_id):
|
def get_data_service_id_from_data_grid_id(datagrid_id):
|
||||||
return datagrid_id.replace(DataGrid.compute_prefix(), DataService.compute_prefix(), 1)
|
return datagrid_id.replace(DataGrid.compute_prefix(), DataService.compute_prefix(), 1)
|
||||||
|
|
||||||
|
def _mk_edition_cell(self, col_pos, row_index, col_def: DataGridColumnState, is_last):
|
||||||
|
col_array = self._fast_access.get(col_def.col_id)
|
||||||
|
value = col_array[row_index] if col_array is not None and row_index < len(col_array) else None
|
||||||
|
value_str = str(value) if not is_null(value) else ""
|
||||||
|
|
||||||
|
save_cmd = self.commands.save_edition()
|
||||||
|
input_elem = mk.mk(
|
||||||
|
Input(value=value_str, name="value", autofocus=True, cls="dt2-cell-input"),
|
||||||
|
command=save_cmd
|
||||||
|
)
|
||||||
|
|
||||||
|
return OptimizedDiv(
|
||||||
|
input_elem,
|
||||||
|
id=self._get_element_id_from_pos("cell", (col_pos, row_index)),
|
||||||
|
cls=merge_classes("dt2-cell dt2-cell-edition", "dt2-last-cell" if is_last else None),
|
||||||
|
style=f"width:{col_def.width}px;"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mk_cell_oob(self, col_pos, row_index):
|
||||||
|
col_def = self._columns[col_pos]
|
||||||
|
filter_keyword = self._state.filtered.get(FILTER_INPUT_CID)
|
||||||
|
filter_keyword_lower = filter_keyword.lower() if filter_keyword else None
|
||||||
|
is_last = col_pos == len(self._columns) - 1
|
||||||
|
return self.mk_body_cell(col_pos, row_index, col_def, filter_keyword_lower, is_last)
|
||||||
|
|
||||||
def mk_headers(self):
|
def mk_headers(self):
|
||||||
resize_cmd = self.commands.set_column_width()
|
resize_cmd = self.commands.set_column_width()
|
||||||
move_cmd = self.commands.move_column()
|
move_cmd = self.commands.move_column()
|
||||||
@@ -867,6 +961,10 @@ class DataGrid(MultipleInstance):
|
|||||||
if col_def.type == ColumnType.RowSelection_:
|
if col_def.type == ColumnType.RowSelection_:
|
||||||
return OptimizedDiv(cls="dt2-row-selection")
|
return OptimizedDiv(cls="dt2-row-selection")
|
||||||
|
|
||||||
|
if (self._settings.enable_edition and
|
||||||
|
self._state.edition.under_edition == (col_pos, row_index)):
|
||||||
|
return self._mk_edition_cell(col_pos, row_index, col_def, is_last)
|
||||||
|
|
||||||
col_array = self._fast_access.get(col_def.col_id)
|
col_array = self._fast_access.get(col_def.col_id)
|
||||||
value = col_array[row_index] if col_array is not None and row_index < len(col_array) else None
|
value = col_array[row_index] if col_array is not None and row_index < len(col_array) else None
|
||||||
content = self.mk_body_cell_content(col_pos, row_index, col_def, filter_keyword_lower)
|
content = self.mk_body_cell_content(col_pos, row_index, col_def, filter_keyword_lower)
|
||||||
@@ -1105,7 +1203,7 @@ class DataGrid(MultipleInstance):
|
|||||||
:param kwargs: Additional parameters for specific fragments (col_id, optimal_width for header)
|
:param kwargs: Additional parameters for specific fragments (col_id, optimal_width for header)
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
res = []
|
res = [self.mk_selection_manager()]
|
||||||
|
|
||||||
extra_attr = {
|
extra_attr = {
|
||||||
"hx-on::after-settle": f"initDataGrid('{self._id}');",
|
"hx-on::after-settle": f"initDataGrid('{self._id}');",
|
||||||
@@ -1133,7 +1231,21 @@ class DataGrid(MultipleInstance):
|
|||||||
header.attrs.update(header_extra_attr)
|
header.attrs.update(header_extra_attr)
|
||||||
return header
|
return header
|
||||||
|
|
||||||
res.append(self.mk_selection_manager())
|
else:
|
||||||
|
col_pos, row_index = None, None
|
||||||
|
|
||||||
|
if (cell_id := kwargs.get("cell_id")) is not None:
|
||||||
|
col_pos, row_index = self._get_pos_from_element_id(cell_id)
|
||||||
|
elif (pos := kwargs.get("pos")) is not None:
|
||||||
|
col_pos, row_index = pos
|
||||||
|
|
||||||
|
if col_pos is not None and row_index is not None:
|
||||||
|
col_def = self._columns[col_pos]
|
||||||
|
filter_keyword = self._state.filtered.get(FILTER_INPUT_CID)
|
||||||
|
filter_keyword_lower = filter_keyword.lower() if filter_keyword else None
|
||||||
|
is_last_col = col_pos == len(self._columns) - 1
|
||||||
|
cell = self.mk_body_cell(col_pos, row_index, col_def, filter_keyword_lower, is_last_col)
|
||||||
|
res.append(cell)
|
||||||
|
|
||||||
return tuple(res)
|
return tuple(res)
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ class Mouse(MultipleInstance):
|
|||||||
|
|
||||||
VALID_ACTIONS = {
|
VALID_ACTIONS = {
|
||||||
'click', 'right_click', 'rclick',
|
'click', 'right_click', 'rclick',
|
||||||
'mousedown>mouseup', 'rmousedown>mouseup'
|
'mousedown>mouseup', 'rmousedown>mouseup',
|
||||||
|
'dblclick', 'double_click', 'dclick'
|
||||||
}
|
}
|
||||||
VALID_MODIFIERS = {'ctrl', 'shift', 'alt'}
|
VALID_MODIFIERS = {'ctrl', 'shift', 'alt'}
|
||||||
def __init__(self, parent, _id=None, combinations=None):
|
def __init__(self, parent, _id=None, combinations=None):
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ class Command:
|
|||||||
# Set the hx-swap-oob attribute on all elements returned by the callback
|
# Set the hx-swap-oob attribute on all elements returned by the callback
|
||||||
if self._htmx_extra[AUTO_SWAP_OOB]:
|
if self._htmx_extra[AUTO_SWAP_OOB]:
|
||||||
for index, r in enumerate(all_ret[1:]):
|
for index, r in enumerate(all_ret[1:]):
|
||||||
if hasattr(r, "__ft__"):
|
if not hasattr(r, 'attrs') and hasattr(r, "__ft__"):
|
||||||
r = r.__ft__()
|
r = r.__ft__()
|
||||||
all_ret[index + 1] = r
|
all_ret[index + 1] = r
|
||||||
if (hasattr(r, 'attrs')
|
if (hasattr(r, 'attrs')
|
||||||
|
|||||||
@@ -318,7 +318,7 @@ class TestDataGridBehaviour:
|
|||||||
dg = datagrid
|
dg = datagrid
|
||||||
dg._state.selection.selected = (1, 2)
|
dg._state.selection.selected = (1, 2)
|
||||||
|
|
||||||
dg.on_click("click", is_inside=False, cell_id=f"tcell_{dg._id}-1-2")
|
dg.handle_on_click("click", is_inside=False, cell_id=f"tcell_{dg._id}-1-2")
|
||||||
|
|
||||||
assert dg._state.selection.selected == (1, 2)
|
assert dg._state.selection.selected == (1, 2)
|
||||||
|
|
||||||
@@ -332,7 +332,7 @@ class TestDataGridBehaviour:
|
|||||||
dg = datagrid
|
dg = datagrid
|
||||||
cell_id = f"tcell_{dg._id}-2-5"
|
cell_id = f"tcell_{dg._id}-2-5"
|
||||||
|
|
||||||
dg.on_click("click", is_inside=True, cell_id=cell_id)
|
dg.handle_on_click("click", is_inside=True, cell_id=cell_id)
|
||||||
|
|
||||||
assert dg._state.selection.selected == (2, 5)
|
assert dg._state.selection.selected == (2, 5)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user