# DataTable

*Display and interact with large datasets using high-performance data tables with sorting, filtering, [pagination](../03_Common/09_Pagination.md), and real-time updates powered by Apache Arrow.*

The `DataTable` [widget](../../01_Onboarding/02_Concepts/03_Widgets.md) provides a powerful, high-performance solution for displaying tabular data. Use it inside [views](../../01_Onboarding/02_Concepts/02_Views.md) and [layouts](../../01_Onboarding/02_Concepts/04_Layout.md), with [state](../../03_Hooks/02_Core/03_UseState.md) for dynamic data or row actions. Built on Apache Arrow for optimal performance with large datasets, it supports automatic type detection, sorting, filtering, column grouping, and customization.

## Basic Usage

Create a DataTable from any `IQueryable<T>` using the `.ToDataTable()` extension method:

```csharp
sampleUsers.ToDataTable()
    .Header(u => u.Name, "Full Name")
    .Header(u => u.Email, "Email Address")
    .Header(u => u.Salary, "Salary")
    .Header(u => u.Status, "Status")
    .Height(Size.Units(100))
```

## Column Configuration

Customize column appearance and behavior with a fluent API:

```csharp
sampleUsers.ToDataTable()
    .Header(u => u.Name, "Full Name")
    .Header(u => u.Email, "Email Address")
    .Header(u => u.Salary, "Annual Salary")
    .Header(u => u.Status, "Status")
    .Width(u => u.Name, Size.Units(50))
    .Width(u => u.Email, Size.Units(60))
    .Width(u => u.Salary, Size.Units(80))
    .Align(u => u.Salary, Align.Right)
    .Icon(u => u.Name, Icons.User.ToString())
    .Icon(u => u.Email, Icons.Mail.ToString())
    .Icon(u => u.Salary, Icons.DollarSign.ToString())
    .Icon(u => u.Status, Icons.Activity.ToString())
    .Sortable(u => u.Email, false)
    .SortDirection(u => u.Salary, SortDirection.Descending)
    .Help(u => u.Name, "Employee full name")
    .Help(u => u.Salary, "Annual salary in USD")
    .Height(Size.Units(100))
```

**Column customization methods:**

- **Header** - Set custom column header text
- **Width** - Set column width using [Size](../../04_ApiReference/IvyShared/Size.md) (e.g. `Size.Px()`, `Size.Percent()`).
- **Align** - Control text alignment ([Align](../../04_ApiReference/IvyShared/Align.md): Left, Right, Center)
- **Icon** - Add an icon to the column header
- **Help** - Add tooltip help text to the column header
- **Sortable** - Enable or disable sorting for specific columns
- **SortDirection** - Set default sort direction (Ascending, Descending, None)
- **Filterable** - Enable or disable filtering for specific columns
- **Hidden** - Hide columns from display
- **Order** - Control the display order of columns
- **Group** - Organize columns into logical groups (requires `ShowGroups` config)

## Advanced Configuration

Use the `.Config()` method to control table behavior and user interactions:

```csharp
sampleUsers.ToDataTable()
    .Header(u => u.Name, "Name")
    .Header(u => u.Email, "Email")
    .Header(u => u.Salary, "Salary")
    .Header(u => u.Status, "Status")
    .Group(u => u.Name, "Personal")
    .Group(u => u.Email, "Personal")
    .Group(u => u.Salary, "Employment")
    .Group(u => u.Status, "Employment")
    .Config(config =>
    {
        config.ShowGroups = true;
        config.ShowIndexColumn = true;
        config.FreezeColumns = 1;
        config.SelectionMode = SelectionModes.Rows;
        config.AllowCopySelection = true;
        config.AllowColumnReordering = true;
        config.AllowColumnResizing = true;
        config.AllowLlmFiltering = true;
        config.AllowSorting = true;
        config.AllowFiltering = true;
        config.ShowSearch = true;
        config.EnableCellClickEvents = true;
        config.ShowVerticalBorders = false;
    })
    .Height(Size.Units(100))
```

**Configuration options:**

- **ShowGroups** - Display column group headers
- **ShowIndexColumn** - Show row index numbers in the first column
- **FreezeColumns** - Number of columns to freeze (remain visible when scrolling horizontally)
- **SelectionMode** - How users can select data (None, Cells, Rows, Columns)
- **AllowCopySelection** - Enable copying selected cells to clipboard
- **AllowColumnReordering** - Allow users to drag and reorder columns
- **AllowColumnResizing** - Allow users to resize column widths
- **AllowLlmFiltering** - Enable AI-powered natural language filtering
- **AllowSorting** - Enable/disable sorting globally
- **AllowFiltering** - Enable/disable filtering globally
- **ShowSearch** - Enable search functionality (accessible via Ctrl/Cmd + F keyboard shortcut)
- **EnableCellClickEvents** - Enable cell click and activation events. When enabled, you can handle `OnCellClick` (single-click) and `OnCellActivated` (double-click) events on the DataTable widget. Events provide `CellClickEventArgs` with `RowIndex`, `ColumnIndex`, `ColumnName`, and `CellValue`.
- **ShowVerticalBorders** - Show vertical borders between columns. Set to `false` to hide column borders for a cleaner appearance

## Row Actions

Add contextual actions to each row using `RowActions()` and handle them via `HandleRowAction()`. Actions are rendered as icons or [buttons](../03_Common/01_Button.md) that appear when hovering over a row. Row actions support both simple menu items and nested [dropdown menus](../03_Common/11_DropDownMenu.md).

Use `RowActions()` to define one or more `MenuItem` objects. Each menu item can have an icon, label, tooltip, and tag. For nested menus, use `.Children()` to create a dropdown menu with sub-items. For example, you can create individual action buttons like edit, delete, or view, as well as a menu button with a dropdown containing additional actions like archive, export, or share.

When creating the DataTable, specify an ID selector using `.ToDataTable(idSelector: e => e.Id)` where `Id` is the property that uniquely identifies each row. This allows the row action handler to identify which row was clicked.

Use `HandleRowAction()` to respond to row action menu selections. The handler receives an `Event<DataTable, RowActionClickEventArgs>` containing:

- **Id** - The ID of the row (extracted using the `idSelector` parameter passed to `ToDataTable()`)
- **Tag** - The tag of the menu item that was clicked (useful for identifying which action was selected, especially with nested menus)

The handler can access both properties: `args.Id` to identify the row and `args.Tag` to determine which action was selected. This is particularly useful when handling nested menu items, as each child menu item can have its own tag.

> **tip:** Use <code>Renderer(expr, new LinkDisplayRenderer { Type = LinkDisplayType.Url })</code> to mark a URL string column as a clickable hyperlink. Click on a link to open it. External links (http/https) open in a new focused tab, while relative URLs navigate in the same tab.

## Cell Click Events

Enable single- and double-click handlers for any cell by setting `EnableCellClickEvents = true` in the table configuration and wiring up `.OnCellClick()` and `.OnCellActivated()` delegates. See [event handling](../../01_Onboarding/02_Concepts/05_EventHandlers.md) for patterns.

The `OnCellClick()` handler is triggered on a single click, while `OnCellActivated()` is triggered on a double-click. Both handlers receive an `Event<DataTable, CellClickEventArgs>` containing:

- **RowIndex** - The zero-based index of the row that was clicked
- **ColumnIndex** - The zero-based index of the column that was clicked
- **ColumnName** - The name of the column that was clicked
- **CellValue** - The value of the cell that was clicked

These properties allow you to perform context-specific actions based on which cell was interacted with. For example, you can display a toast notification with the column name and row index, or navigate to a detail view based on the cell's value.

## AI-Powered Filtering

DataTable supports natural language filtering powered by a Large Language Model (LLM). Instead of writing formal filter expressions, users can type conversational queries and the AI will convert them to the appropriate filter syntax.

### Enabling AI Filtering

Enable AI-powered filtering with a single configuration option:

```csharp
public record Employee(int Id, string Name, decimal Salary, bool IsActive);

[App]
public class EmployeeTableApp : ViewBase
{
    public override object? Build()
    {
        var employees = GetEmployees().AsQueryable();
        return employees.ToDataTable(e => e.Id)
            .Header(e => e.Name, "Employee Name")
            .Header(e => e.Salary, "Salary")
            .Header(e => e.IsActive, "Active")
            .Width(e => e.Salary, Size.Px(120))
            .Config(config =>
            {
                config.AllowSorting = true;
                config.AllowFiltering = true;
                config.AllowLlmFiltering = true;
                config.BatchSize = 50;
            });
    }
}
```

### Natural Language Queries

Once enabled with `.Config(config => config.AllowLlmFiltering = true)`, users can filter using conversational phrases:

- "employees older than 30"
- "salary above 100000"
- "active managers"
- "hired in 2023"

### Smart Interpretation

The AI agent provides intelligent query handling:

- **Typo tolerance** - Automatically corrects misspellings
- **Concept mapping** - Converts phrases like "retirement age" into structured conditions (e.g., `[Age] >= 65`)
- **Type mismatch resolution** - If a field type doesn't match user intent, the agent identifies appropriate alternative fields

### Supported Filter Grammar

The AI filter agent converts natural language queries to structured filter expressions like `[Age] > 30` or `[Salary] > 100000 AND [IsManager] = true`.

The AI generates filters using these operations:

- **Comparisons:** `=`, `!=`, `>`, `>=`, `<`, `<=`
- **Text operations:** `contains`, `starts with`, `ends with`
- **Existence checks:** `IS BLANK`, `IS NOT BLANK`
- **Logical operators:** `AND`, `OR`, `NOT`
- **Parenthetical grouping** for complex expressions

## DateTime Filtering

`DataTable` fully supports filtering `DateTime`, `Date`, and `DateTimeOffset` columns using ISO-8601 date strings. Users can type expressions such as

```text
[HireDate] = "2024-05-30"
[OrderDate] >= "2025-11-01" AND [OrderDate] <= "2025-11-31"
```

into the filter box (Ctrl/Cmd&nbsp;+&nbsp;F) or the column filter UI, and the underlying dataset will be queried server-side. The parsing engine recognises dates without needing quotes if there are no spaces, but quoting is recommended.

```csharp
public class DateFilterDemo : ViewBase
{
    public record Order(int Id, DateTime OrderDate, DateTime? ShippedDate, int Total);

    public override object? Build()
    {
        var orders = Enumerable.Range(1, 50)
            .Select(i => new Order(
                i,
                OrderDate: DateTime.Today.AddDays(-i),
                ShippedDate: i % 3 == 0 ? DateTime.Today.AddDays(-i + 2) : null,
                Total: 55 + i * 5))
            .AsQueryable();

        return orders
            .ToDataTable()
            .Header(o => o.OrderDate, "Order Date")
            .Header(o => o.ShippedDate, "Shipped")
            .Header(o => o.Total, "Total ($)")
            .Config(c =>
            {
                c.AllowFiltering = true;     // enable filter row + Ctrl/Cmd+F search
                c.ShowSearch = true;
            })
            .Height(Size.Units(100));
    }
}
```

Try filtering the _Order Date_ column with a range such as [OrderDate] >= "2025-11-01" AND [OrderDate] <= "2025-11-31" to see the results update in real time.

## Performance with Large Datasets

DataTable is optimized for handling extremely large datasets efficiently. For optimal performance with large datasets (100,000+ rows), configure how data is loaded:

```csharp
Enumerable.Range(1, 500)
    .Select(i => new { Id = i, Value = $"Row {i}" })
    .AsQueryable()
    .ToDataTable()
    .Header(x => x.Id, "ID")
    .Header(x => x.Value, "Value")
    .LoadAllRows(true)  // Load all rows at once
    .Height(Size.Units(100))
```

**Performance options:**

- **LoadAllRows(true)** - Load all rows at once for maximum performance with very large datasets. Set to `false` to enable incremental loading with batching.
- **BatchSize(n)** - Load data in batches of n rows for incremental loading. Use this when `LoadAllRows` is `false` to control how many rows are loaded per batch. Default is typically 50 rows per batch.

**Example with performance configuration:**

```csharp
sampleUsers.ToDataTable()
    .Header(u => u.Name, "Name")
    .Header(u => u.Email, "Email")
    .Header(u => u.Salary, "Salary")
    .Config(config =>
    {
        config.BatchSize = 50;        // Load 50 rows per batch
        config.LoadAllRows = false;   // Enable incremental loading
    })
    .Height(Size.Units(100))
```

</Body>
</Details>


## API

[View Source: DataTable.cs](https://github.com/Ivy-Interactive/Ivy-Framework/blob/main/src/Ivy/Widgets/DataTables/DataTable.cs)

### Constructors

| Signature |
|-----------|
| `new DataTable(DataTableConnection connection, Size width, Size height, DataTableColumn[] columns, DataTableConfig config)` |


### Properties

| Name | Type | Setters |
|------|------|---------|
| `Columns` | `DataTableColumn[]` | - |
| `Config` | `DataTableConfig` | - |
| `Connection` | `DataTableConnection` | - |
| `Height` | `Size` | - |
| `RowActions` | `MenuItem[]` | - |
| `Scale` | `Scale?` | - |
| `Visible` | `bool` | - |
| `Width` | `Size` | - |


### Events

| Name | Type | Handlers |
|------|------|----------|
| `OnCellActivated` | `Func<Event<DataTable, CellClickEventArgs>, ValueTask>` | - |
| `OnCellClick` | `Func<Event<DataTable, CellClickEventArgs>, ValueTask>` | - |
| `OnRowAction` | `Func<Event<DataTable, RowActionClickEventArgs>, ValueTask>` | - |