# data-view

Data table controller with client-side search, sort, pagination, column visibility, row actions, and async loading.

## Markup

```html
<div data-c42-dataview>
  <!-- Search -->
  <input data-c42-dataview-search placeholder="Search..." />

  <!-- Column toggles (optional) -->
  <label><input type="checkbox" data-c42-dataview-column="name" checked /> Name</label>
  <label><input type="checkbox" data-c42-dataview-column="age" checked /> Age</label>

  <!-- Loading indicator (optional) -->
  <div data-c42-dataview-loading hidden>Loading...</div>

  <!-- Table -->
  <table>
    <thead>
      <tr>
        <th data-col="name"><button data-c42-dataview-sort-field="name">Name</button></th>
        <th data-col="age"><button data-c42-dataview-sort-field="age">Age</button></th>
        <th>Actions</th>
      </tr>
    </thead>
    <tbody data-c42-dataview-rows>
      <tr data-c42-dataview-row data-search="Alice" data-field-name="Alice" data-field-age="25">
        <td data-col="name">Alice</td>
        <td data-col="age">25</td>
        <td><button data-c42-dataview-action="edit">Edit</button></td>
      </tr>
      <!-- more rows... -->
    </tbody>
  </table>

  <!-- Pagination -->
  <button data-c42-dataview-prev>Prev</button>
  <span data-c42-dataview-page></span> / <span data-c42-dataview-pages></span>
  <button data-c42-dataview-next>Next</button>
  <span data-c42-dataview-info></span>
</div>
```

## Key data attributes on rows

| Attribute | Purpose |
|-----------|---------|
| `data-c42-dataview-row` | Marks a row for the controller |
| `data-search="text"` | Text used for client-side filtering |
| `data-field-<key>="value"` | Sortable field value (compared as string or number) |

## Column visibility

Each `<th>` and `<td>` with `data-col="key"` will be hidden/shown when the matching `data-c42-dataview-column` checkbox is toggled.

## Options

```ts
import { DataView } from '@42/core/data-view';

new DataView(root, {
  pageSize: 10,           // rows per page
  defaultSort: 'name:asc', // initial sort (field:order)
  defaultQuery: '',       // initial search
  total: undefined,       // server-driven mode: provide total externally
  loading: false,         // start in loading state
  visibleColumns: undefined, // array of column keys, or all visible
});
```

## Events

| Event | Detail |
|-------|--------|
| `dataview:change` | `{ query, sortField, sortOrder, page, pageSize, total, totalPages, loading, visibleColumns }` |
| `dataview:row-action` | `{ action: string, row: HTMLElement, rowIndex: number }` |

## Async / server-driven mode

1. Start with `loading: true` and an empty `<tbody>`.
2. Listen to `dataview:change` — it fires on search/sort/page changes.
3. Fetch data from your API using the state from the event detail.
4. Replace `<tbody>` innerHTML with new rows.
5. Call `instance.setLoading(false)` to hide the loading indicator.

```ts
const dv = new DataView(root, { loading: true, total: 100, pageSize: 10 });
dv.on('dataview:change', async (e) => {
  const { query, sortField, sortOrder, page, pageSize } = e.detail;
  const data = await fetchFromAPI({ query, sortField, sortOrder, page, pageSize });
  tbody.innerHTML = renderRows(data.items);
  dv.setLoading(false);
});
```

## Methods

| Method | Description |
|--------|-------------|
| `setLoading(bool)` | Show/hide loading indicator and re-scan rows |
| `destroy()` | Remove all listeners |
