# DataTable Widget API Reference

## Overview

The DataTable widget displays tabular data with support for selection, scrolling, and styling.

## Import

```python
from textual.widgets import DataTable
```

## Class Hierarchy

```
Widget → ScrollView → DataTable
```

## Constructor

```python
DataTable(
    *,
    show_header: bool = True,
    fixed_rows: int = 0,
    fixed_columns: int = 0,
    zebra_stripes: bool = False,
    header_height: int = 1,
    show_cursor: bool = True,
    cursor_foreground_priority: Literal["renderable", "css"] = "renderable",
    cursor_background_priority: Literal["renderable", "css"] = "renderable",
    cursor_type: CursorType = "cell",
    cell_padding: int = 1,
    name: str | None = None,
    id: str | None = None,
    classes: str | None = None,
    disabled: bool = False
)
```

## Key Properties

### Cursor Properties
- `cursor_row: int` - Current cursor row (0-based)
- `cursor_column: int` - Current cursor column (0-based)
- `cursor_coordinate: Coordinate` - (row, column) tuple

### Table Properties
- `rows: Mapping[RowKey, Row]` - Dictionary of rows
- `columns: list[Column]` - List of columns
- `row_count: int` - Number of rows
- `column_count: int` - Number of columns

## Methods

### Adding Data

```python
# Add columns
add_column(label: TextType, *, width: int | None = None, key: str | None = None, default: CellType | None = None) -> ColumnKey

# Add multiple columns
add_columns(*labels: TextType) -> list[ColumnKey]

# Add a single row
add_row(*cells: CellType, height: int | None = 1, key: str | None = None, label: TextType | None = None) -> RowKey

# Add multiple rows
add_rows(rows: Iterable[Iterable[CellType]]) -> list[RowKey]
```

### Modifying Data

```python
# Update a cell
update_cell(row_key: RowKey, column_key: ColumnKey, value: CellType, *, update_width: bool = False) -> None

# Update cell at coordinate
update_cell_at(coordinate: Coordinate, value: CellType, *, update_width: bool = False) -> None

# Remove row
remove_row(row_key: RowKey) -> None

# Remove column  
remove_column(column_key: ColumnKey) -> None

# Clear all data
clear(columns: bool = False) -> Self
```

### Navigation

```python
# Move cursor
move_cursor(row: int | None = None, column: int | None = None, animate: bool = True, scroll: bool = True) -> None

# Scroll to coordinate
scroll_to(x: float | None = None, y: float | None = None, *, animate: bool = True, speed: float | None = None, duration: float | None = None, ...) -> None
```

### Sorting

```python
# Sort by column(s)
sort(*columns: ColumnKey | str, reverse: bool = False) -> Self

# Sort with custom key
sort(key: Callable[[Any], Any], *, reverse: bool = False) -> Self
```

## Events

### Selection Events

```python
class RowSelected(Message):
    """Posted when a row is selected (Enter key)."""
    row_key: RowKey
    row_index: int

class CellSelected(Message):
    """Posted when a cell is selected."""
    coordinate: Coordinate
    cell_key: CellKey

class RowHighlighted(Message):
    """Posted when cursor highlights a row."""
    row_key: RowKey
    row_index: int

class CellHighlighted(Message):
    """Posted when cursor highlights a cell."""
    coordinate: Coordinate
    cell_key: CellKey

class ColumnHighlighted(Message):
    """Posted when cursor highlights a column."""
    column_key: ColumnKey
    column_index: int
```

### Header Events

```python
class HeaderSelected(Message):
    """Posted when a column header is clicked."""
    column_key: ColumnKey
    column_index: int
    label: Text

class RowLabelSelected(Message):
    """Posted when a row label is clicked."""
    row_key: RowKey
    row_index: int
    label: Text
```

## Common Patterns

### Creating a DataTable

```python
class MyWidget(Widget):
    def compose(self) -> ComposeResult:
        yield DataTable(id="my-table")
    
    def on_mount(self) -> None:
        table = self.query_one("#my-table", DataTable)
        table.add_columns("Name", "Value", "Status")
        table.add_row("Item 1", "100", "Active", key="item_1")
```

### Handling Selection

```python
def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
    # React to cursor movement
    self.selected_row = event.row_key

def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
    # React to Enter key press
    self.process_selection(event.row_key)
```

### Preserving Cursor Position

```python
def refresh_table(self):
    table = self.query_one(DataTable)
    
    # Save position
    cursor_row = table.cursor_row
    selected_key = list(table.rows.keys())[cursor_row] if table.rows else None
    
    # Update data
    table.clear()
    # ... add new data ...
    
    # Restore position
    if selected_key and selected_key in table.rows:
        row_index = list(table.rows.keys()).index(selected_key)
        table.move_cursor(row=row_index, animate=False)
```

## Styling

DataTable supports CSS styling but does NOT have:
- `set_row_style()` method
- `set_cell_style()` method

Use CSS classes and selectors instead:

```css
DataTable > .datatable--cursor {
    background: $primary 30%;
}

DataTable > .datatable--header {
    text-style: bold;
}
```

## Type Hints

```python
from textual.widgets.data_table import RowKey, ColumnKey, CellKey
from textual.coordinate import Coordinate

# RowKey and ColumnKey are not strings - convert with str() if needed
row_key_str = str(row_key)
```

## Common Errors and Solutions

1. **TypeError: 'RowKey' is not iterable**
   - Solution: Convert to string first: `str(row_key)`

2. **AttributeError: 'DataTable' has no attribute 'set_row_style'**
   - Solution: Use CSS styling instead

3. **Event not firing**
   - Check handler name: `on_data_table_row_highlighted` not `on_data_table_cursor_moved`