diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2124f2dd3..6bc2f69e9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
-
## Unreleased
### Added
+- Added `DataTable.remove_row` method https://github.com/Textualize/textual/pull/2253
- `Widget.scroll_to_center` now scrolls the widget to the center of the screen https://github.com/Textualize/textual/pull/2255
## [0.19.1] - 2023-04-10
diff --git a/src/textual/_two_way_dict.py b/src/textual/_two_way_dict.py
index d733edcdc..123e1848d 100644
--- a/src/textual/_two_way_dict.py
+++ b/src/textual/_two_way_dict.py
@@ -29,6 +29,9 @@ class TwoWayDict(Generic[Key, Value]):
self._forward.__delitem__(key)
self._reverse.__delitem__(value)
+ def __iter__(self):
+ return iter(self._forward)
+
def get(self, key: Key) -> Value:
"""Given a key, efficiently lookup and return the associated value.
diff --git a/src/textual/widgets/_data_table.py b/src/textual/widgets/_data_table.py
index 369c11778..5e9a0321e 100644
--- a/src/textual/widgets/_data_table.py
+++ b/src/textual/widgets/_data_table.py
@@ -308,7 +308,9 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
cursor_coordinate: Reactive[Coordinate] = Reactive(
Coordinate(0, 0), repaint=False, always_update=True
)
- hover_coordinate: Reactive[Coordinate] = Reactive(Coordinate(0, 0), repaint=False)
+ hover_coordinate: Reactive[Coordinate] = Reactive(
+ Coordinate(0, 0), repaint=False, always_update=True
+ )
class CellHighlighted(Message, bubble=True):
"""Posted when the cursor moves to highlight a new cell.
@@ -1186,6 +1188,10 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
self._label_column = Column(self._label_column_key, Text(), auto_width=True)
self._labelled_row_exists = False
self.refresh()
+ self.scroll_x = 0
+ self.scroll_y = 0
+ self.scroll_target_x = 0
+ self.scroll_target_y = 0
return self
def add_column(
@@ -1322,6 +1328,41 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
row_keys.append(row_key)
return row_keys
+ def remove_row(self, row_key: RowKey | str) -> None:
+ """Remove a row (identified by a key) from the DataTable.
+
+ Args:
+ row_key: The key identifying the row to remove.
+
+ Raises:
+ RowDoesNotExist: If the row key does not exist.
+ """
+ if row_key not in self._row_locations:
+ raise RowDoesNotExist(f"Row key {row_key!r} is not valid.")
+
+ self._require_update_dimensions = True
+ self.check_idle()
+
+ index_to_delete = self._row_locations.get(row_key)
+ new_row_locations = TwoWayDict({})
+ for row_location_key in self._row_locations:
+ row_index = self._row_locations.get(row_location_key)
+ if row_index > index_to_delete:
+ new_row_locations[row_location_key] = row_index - 1
+ elif row_index < index_to_delete:
+ new_row_locations[row_location_key] = row_index
+
+ self._row_locations = new_row_locations
+
+ del self.rows[row_key]
+ del self._data[row_key]
+
+ self.cursor_coordinate = self.cursor_coordinate
+ self.hover_coordinate = self.hover_coordinate
+
+ self._update_count += 1
+ self.refresh(layout=True)
+
def on_idle(self) -> None:
"""Runs when the message pump is empty.
diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
index ab9dc85c3..2f9c7534b 100644
--- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
+++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
@@ -12593,6 +12593,164 @@
'''
# ---
+# name: test_datatable_remove_row
+ '''
+
+
+ '''
+# ---
# name: test_datatable_render
'''