Added keyboard navigation support

This commit is contained in:
2026-03-16 22:43:45 +01:00
parent 2fcc225414
commit 853bc4abae
4 changed files with 160 additions and 1 deletions

View File

@@ -142,6 +142,10 @@
outline-offset: -3px; /* Ensure the outline is snug to the cell */
}
.grid:focus {
outline: none;
}
.dt2-cell:hover,
.dt2-selected-cell {
background-color: var(--color-selection);

View File

@@ -688,6 +688,7 @@ function updateDatagridSelection(datagridId) {
});
// Loop through the children of the selection manager
let hasFocusedCell = false;
Array.from(selectionManager.children).forEach((selection) => {
const selectionType = selection.getAttribute('selection-type');
const elementId = selection.getAttribute('element-id');
@@ -697,6 +698,8 @@ function updateDatagridSelection(datagridId) {
if (cellElement) {
cellElement.classList.add('dt2-selected-focus');
cellElement.style.userSelect = 'text';
cellElement.focus({ preventScroll: true });
hasFocusedCell = true;
}
} else if (selectionType === 'cell') {
const cellElement = document.getElementById(`${elementId}`);
@@ -744,6 +747,11 @@ function updateDatagridSelection(datagridId) {
}
}
});
if (!hasFocusedCell) {
const grid = document.getElementById(datagridId);
if (grid) grid.focus({ preventScroll: true });
}
}
/**

View File

@@ -227,6 +227,13 @@ class Commands(BaseCommands):
class DataGrid(MultipleInstance):
_ARROW_KEY_DIRECTIONS = {
"arrowright": "right",
"arrowleft": "left",
"arrowdown": "down",
"arrowup": "up",
}
def __init__(self, parent, conf=None, save_state=None, _id=None):
super().__init__(parent, _id=_id)
name, namespace = (conf.name, conf.namespace) if conf else ("No name", "__default__")
@@ -311,6 +318,10 @@ class DataGrid(MultipleInstance):
self._key_support = {
"esc": {"command": self.commands.on_key_pressed(), "require_inside": False},
"enter": {"command": self.commands.on_key_pressed(), "require_inside": True},
"arrowup": {"command": self.commands.on_key_pressed(), "require_inside": True},
"arrowdown": {"command": self.commands.on_key_pressed(), "require_inside": True},
"arrowleft": {"command": self.commands.on_key_pressed(), "require_inside": True},
"arrowright": {"command": self.commands.on_key_pressed(), "require_inside": True},
}
logger.debug(f"DataGrid '{self.get_table_name()}' with id='{self._id}' created.")
@@ -387,6 +398,37 @@ class DataGrid(MultipleInstance):
else:
return f"tcell_{self._id}-{pos[0]}-{pos[1]}"
def _get_navigable_col_positions(self) -> list[int]:
return [i for i, col in enumerate(self._columns)
if col.visible and col.type != ColumnType.RowSelection_]
def _get_visible_row_indices(self) -> list[int]:
df = self._get_filtered_df()
return list(df.index) if df is not None else []
def _navigate(self, pos: tuple, direction: str) -> tuple:
col_pos, row_index = pos
navigable_cols = self._get_navigable_col_positions()
visible_rows = self._get_visible_row_indices()
if not navigable_cols or not visible_rows:
return pos
if direction == "right":
next_cols = [c for c in navigable_cols if c > col_pos]
return (next_cols[0], row_index) if next_cols else pos
elif direction == "left":
prev_cols = [c for c in navigable_cols if c < col_pos]
return (prev_cols[-1], row_index) if prev_cols else pos
elif direction == "down":
next_rows = [r for r in visible_rows if r > row_index]
return (col_pos, next_rows[0]) if next_rows else pos
elif direction == "up":
prev_rows = [r for r in visible_rows if r < row_index]
return (col_pos, prev_rows[-1]) if prev_rows else pos
return pos
def _get_pos_from_element_id(self, element_id):
if element_id is None:
return None
@@ -650,6 +692,13 @@ class DataGrid(MultipleInstance):
self._state.selection.selected and
self._state.edition.under_edition is None):
return self._enter_edition(self._state.selection.selected)
elif combination in self._ARROW_KEY_DIRECTIONS:
current_pos = (self._state.selection.selected
or self._state.selection.last_selected
or (0, 0))
direction = self._ARROW_KEY_DIRECTIONS[combination]
new_pos = self._navigate(current_pos, direction)
self._update_current_position(new_pos)
return self.render_partial()
@@ -974,6 +1023,7 @@ class DataGrid(MultipleInstance):
data_tooltip=str(value),
style=f"width:{col_def.width}px;",
id=self._get_element_id_from_pos("cell", (col_pos, row_index)),
tabindex="-1",
cls=merge_classes("dt2-cell", "dt2-last-cell" if is_last else None))
def mk_row(self, row_index, filter_keyword_lower, len_columns_1):
@@ -1193,7 +1243,8 @@ class DataGrid(MultipleInstance):
),
id=self._id,
cls="grid",
style="height: 100%; grid-template-rows: auto 1fr;"
style="height: 100%; grid-template-rows: auto 1fr;",
tabindex="-1"
)
def render_partial(self, fragment="cell", **kwargs):