# MasterDetailPlugin

> _Since v0.1.1_

Master-Detail Plugin for tbw-grid

Creates expandable detail rows that reveal additional content beneath each master row.
Perfect for order/line-item UIs, employee/department views, or any scenario where
you need to show related data without navigating away.

## Installation

```ts
import { MasterDetailPlugin } from '@toolbox-web/grid/plugins/master-detail';
```

## CSS Custom Properties

| Property | Default | Description |
|----------|---------|-------------|
| `--tbw-master-detail-bg` | `var(--tbw-color-row-alt)` | Detail row background |
| `--tbw-master-detail-border` | `var(--tbw-color-border)` | Detail row border |
| `--tbw-detail-padding` | `1em` | Detail content padding |
| `--tbw-animation-duration` | `200ms` | Expand/collapse animation |

## [Configuration Options](/grid/plugins/master-detail/interfaces/masterdetailconfig.md)

| Option | Type | Description |
| ------ | ---- | ----------- |
| `detailRenderer?` | <code>(row: Record&lt;string, unknown&gt;, rowIndex: number) =&gt; string &#124; HTMLElement</code> | Renderer function that returns detail content for a row |
| `detailHeight?` | <code>number &#124; auto</code> | Height of the detail row - number (pixels) or 'auto' (default: 'auto') |
| `expandOnRowClick?` | <code>boolean</code> | Expand/collapse detail on row click (default: false) |
| `showExpandColumn?` | <code>boolean</code> | Show expand/collapse column (default: true) |
| `animation?` | <code><a href="/grid/api/core/types/expandcollapseanimation/">ExpandCollapseAnimation</a></code> | Animation style for expanding/collapsing detail rows. - `false`: No animation - `'slide'`: Slide down/up animation (default) - `'fade'`: Fade in/out animation |

## Examples

### Basic Master-Detail with HTML Template

```ts
import '@toolbox-web/grid';
import { MasterDetailPlugin } from '@toolbox-web/grid/plugins/master-detail';

grid.gridConfig = {
  columns: [
    { field: 'orderId', header: 'Order ID' },
    { field: 'customer', header: 'Customer' },
    { field: 'total', header: 'Total', type: 'currency' },
  ],
  plugins: [
    new MasterDetailPlugin({
      detailRenderer: (row) => `
        <div class="order-details">
          <h4>Order Items</h4>
          <ul>${row.items.map(i => `<li>${i.name} - $${i.price}</li>`).join('')}</ul>
        </div>
      `,
    }),
  ],
};
```

### Nested Grid in Detail

```ts
new MasterDetailPlugin({
  detailRenderer: (row) => {
    const childGrid = document.createElement('tbw-grid');
    childGrid.style.height = '200px';
    childGrid.gridConfig = { columns: [...] };
    childGrid.rows = row.items || [];
    return childGrid;
  },
})
```

## See Also

- [`MasterDetailConfig`](/grid/plugins/master-detail/interfaces/masterdetailconfig.md) for all configuration options
- [`DetailExpandDetail`](/grid/plugins/master-detail/interfaces/detailexpanddetail.md) for expand/collapse event details

> **Extends** [BaseGridPlugin](/docs/grid-api-plugin-development-classes-basegridplugin--docs)
>
> Inherited methods like `attach()`, `detach()`, `afterRender()`, etc. are documented in the base class.

## Properties

| Property | Type | Description |
| -------- | ---- | ----------- |
| `dependencies` | <code>object[]</code> | Optional dependency on ServerSide for async detail data. When loaded, MasterDetail can fetch detail data via `datasource:fetch-children`. |

## Methods

### getRowHeight()

`override` — Get the height of a specific row, including any expanded detail content.
Always returns a height to ensure the position cache uses plugin-controlled values
rather than stale DOM measurements.

```ts
getRowHeight(row: unknown, _index: number): number | undefined
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `row` | <code>unknown</code> | The row data |
| `_index` | <code>number</code> | The row index (unused, but part of the interface) |

#### Returns

`number | undefined` - The row height in pixels (base height for collapsed, base + detail for expanded)

***

### adjustVirtualStart()

`override` — Adjust the virtualization start index to keep expanded row visible while its detail is visible.
This ensures the detail scrolls smoothly out of view instead of disappearing abruptly.

```ts
adjustVirtualStart(start: number, scrollTop: number, rowHeight: number): number
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `start` | <code>number</code> |  |
| `scrollTop` | <code>number</code> |  |
| `rowHeight` | <code>number</code> |  |

***

### expand()

Expand the detail row at the given index.

```ts
expand(rowIndex: number): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `rowIndex` | <code>number</code> | Index of the row to expand |

***

### collapse()

Collapse the detail row at the given index.

```ts
collapse(rowIndex: number): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `rowIndex` | <code>number</code> | Index of the row to collapse |

***

### toggle()

Toggle the detail row at the given index.

```ts
toggle(rowIndex: number): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `rowIndex` | <code>number</code> | Index of the row to toggle |

***

### isExpanded()

Check if the detail row at the given index is expanded.

```ts
isExpanded(rowIndex: number): boolean
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `rowIndex` | <code>number</code> | Index of the row to check |

#### Returns

`boolean` - Whether the detail row is expanded

***

### expandAll()

Expand all detail rows.
Skips synthetic rows (e.g., group headers from GroupingRowsPlugin).

```ts
expandAll(): void
```

***

### collapseAll()

Collapse all detail rows.

```ts
collapseAll(): void
```

***

### getExpandedRows()

Get the indices of all expanded rows.

```ts
getExpandedRows(): number[]
```

#### Returns

`number[]` - Array of row indices that are expanded

***

### getDetailElement()

Get the detail element for a specific row.

```ts
getDetailElement(rowIndex: number): HTMLElement | undefined
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `rowIndex` | <code>number</code> | Index of the row |

#### Returns

`HTMLElement | undefined` - The detail HTMLElement or undefined

***

### getDetailData()

Get async detail data fetched via `ServerSidePlugin` for a specific row.

Returns the child rows received from `datasource:children` after a
`datasource:fetch-children` query was fired on expand. Returns `undefined`
when ServerSide is not loaded or data has not arrived yet.

```ts
getDetailData(rowIndex: number): unknown[] | undefined
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `rowIndex` | <code>number</code> | Index of the row |

#### Returns

`unknown[] | undefined` - Array of child rows or undefined

***

### isDetailLoading()

Check if detail data is currently being loaded for a row.

```ts
isDetailLoading(rowIndex: number): boolean
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `rowIndex` | <code>number</code> | Index of the row |

#### Returns

`boolean` - Whether the detail is loading

***

### refreshDetailRenderer()

Re-parse light DOM to refresh the detail renderer.
Call this after framework templates are registered (e.g., Angular ngAfterContentInit).

This allows frameworks to register templates asynchronously and then
update the plugin's detailRenderer.

```ts
refreshDetailRenderer(): void
```

***
