# GroupingRowsPlugin

> _Since v0.1.1_

Row Grouping Plugin for tbw-grid

Organizes rows into collapsible hierarchical groups. Perfect for organizing data
by category, department, status, or any other dimension—or even multiple dimensions
for nested grouping. Includes aggregation support for summarizing group data.

## Installation

```ts
import { GroupingRowsPlugin } from '@toolbox-web/grid/plugins/grouping-rows';
```

## CSS Custom Properties

| Property | Default | Description |
|----------|---------|-------------|
| `--tbw-group-indent-width` | `1.25em` | Indentation per group level |
| `--tbw-grouping-rows-bg` | `var(--tbw-color-panel-bg)` | Group row background |
| `--tbw-grouping-rows-count-color` | `var(--tbw-color-fg-muted)` | Count badge color |
| `--tbw-animation-duration` | `200ms` | Expand/collapse animation |

## [Configuration Options](/grid/plugins/grouping-rows/interfaces/groupingrowsconfig.md)

| Option | Type | Description |
| ------ | ---- | ----------- |
| `groupOn?` | <code>(row: any) =&gt; any</code> | Callback to determine group path for a row. Return an array of group keys, a single key, null/false to skip grouping. |
| `groups?` | <code><a href="/grid/plugins/grouping-rows/interfaces/groupdefinition/">GroupDefinition</a>[]</code> | Pre-defined group structure for server-side grouping. |
| `defaultExpanded?` | <code><a href="/grid/plugins/grouping-rows/types/defaultexpandedvalue/">DefaultExpandedValue</a></code> | Default expanded state for group rows. - `true`: Expand all groups initially - `false`: Collapse all groups initially (default) - `number`: Expand group at this index (0-based) - `string`: Expand group with this key (composite key format: "parent||child") - `string[]`: Expand groups with these keys |
| `groupRowRenderer?` | <code>(params: <a href="/grid/plugins/grouping-rows/interfaces/grouprowrenderparams/">GroupRowRenderParams</a>) =&gt; string &#124; void &#124; HTMLElement</code> | Custom group row renderer - takes full control of group row rendering |
| `showRowCount?` | <code>boolean</code> | Show row count in group headers (default: true) |
| `indentWidth?` | <code>number</code> | Indent width per depth level in pixels (default: 20) |
| `aggregators?` | <code><a href="/grid/plugins/grouping-rows/types/aggregatormap/">AggregatorMap</a></code> | Aggregators for group row cells by field name |
| `formatLabel?` | <code>(value: any, depth: number, key: string) =&gt; string</code> | Custom format function for group label |
| `fullWidth?` | <code>boolean</code> | Whether to render group row as full-width spanning cell (default: true) |
| `animation?` | <code><a href="/grid/api/core/types/expandcollapseanimation/">ExpandCollapseAnimation</a></code> | Animation style for expanding/collapsing groups. - `false`: No animation - `'slide'`: Slide animation (default) - `'fade'`: Fade animation |
| `accordion?` | <code>boolean</code> | Accordion mode - only one group can be expanded at a time. Expanding a group will automatically collapse all other groups at the same depth. |
| `groupRowHeight?` | <code>number</code> | Height of group header rows in pixels. Used by the variable row height system to provide consistent heights for group rows without needing to measure them. |

## Examples

### Single-Level Grouping by Department

```ts
import { queryGrid } from '@toolbox-web/grid';
import { GroupingRowsPlugin } from '@toolbox-web/grid/plugins/grouping-rows';

const grid = queryGrid('tbw-grid');
grid.gridConfig = {
  columns: [
    { field: 'name', header: 'Employee' },
    { field: 'department', header: 'Department' },
    { field: 'salary', header: 'Salary', type: 'currency' },
  ],
  plugins: [
    new GroupingRowsPlugin({
      groupOn: (row) => [row.department],
      showRowCount: true,
      defaultExpanded: false,
    }),
  ],
};
```

### Multi-Level Grouping

```ts
new GroupingRowsPlugin({
  groupOn: (row) => [row.region, row.department, row.team],
  indentWidth: 24,
  animation: 'slide',
})
```

## See Also

- [`GroupingRowsConfig`](/grid/plugins/grouping-rows/interfaces/groupingrowsconfig.md) for all configuration options
- [`GroupState`](/grid/plugins/grouping-rows/interfaces/groupstate.md) for the group state structure

> **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 MultiSort for coordinated sort management. When MultiSort is loaded, GroupingRows queries its sort model to determine group header ordering. Without it, falls back to core sort state. |

## Methods

### detect()

Auto-detect grouping configuration from grid config.
Called by plugin system to determine if plugin should activate.

```ts
static detect(rows: readonly any[], config: any): boolean
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `rows` | <code>readonly any[]</code> |  |
| `config` | <code>any</code> |  |

***

### expandAll()

Expand all groups.

Safe to call immediately after setGroupOn: when the grouping
config has just changed, the cached `flattenedRows` is stale, so the
call defers until the next render rebuilds the group model against the
new `groupOn` (issue #335). The `group-toggle` event is emitted once
the new keys are known.

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

***

### collapseAll()

Collapse all groups.

Safe to call immediately after setGroupOn: defers until the next
render rebuilds the group model so the collapse targets the new groups
rather than a stale snapshot (issue #335).

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

***

### toggle()

Toggle expansion of a specific group.
In accordion mode, expanding a group will collapse all sibling groups.

```ts
toggle(key: string): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `key` | <code>string</code> | The group key to toggle |

***

### isExpanded()

Check if a specific group is expanded.

```ts
isExpanded(key: string): boolean
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `key` | <code>string</code> | The group key to check |

#### Returns

`boolean` - Whether the group is expanded

***

### expand()

Expand a specific group.

```ts
expand(key: string): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `key` | <code>string</code> | The group key to expand |

***

### collapse()

Collapse a specific group.

```ts
collapse(key: string): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `key` | <code>string</code> | The group key to collapse |

***

### getGroupState()

Get the current group state.

```ts
getGroupState(): GroupState
```

#### Returns

`GroupState` - Group state information

***

### getRowCount()

Get the total count of visible rows (including group headers).

```ts
getRowCount(): number
```

#### Returns

`number` - Number of visible rows

***

### refreshGroups()

Refresh the grouped row model.
Call this after modifying groupOn or other config options.

```ts
refreshGroups(): void
```

***

### getExpandedGroups()

Get current expanded group keys.

```ts
getExpandedGroups(): string[]
```

#### Returns

`string[]` - Array of expanded group keys

***

### getFlattenedRows()

Get the flattened row model.

```ts
getFlattenedRows(): RenderRow[]
```

#### Returns

`RenderRow[]` - Array of render rows (groups + data rows)

***

### isGroupingActive()

Check if grouping is currently active.

```ts
isGroupingActive(): boolean
```

#### Returns

`boolean` - Whether grouping is active

***

### setGroupOn()

Set the groupOn function dynamically.

Optionally specify the initial expansion state to apply against the new
group set. Without it, the previous `expandedKeys` are kept (and most
likely won't match the new keys, so groups land collapsed). With it,
the new groups are expanded synchronously on the next render — no need
to chain `setGroupOn(fn); expandAll()` or wait for `requestAnimationFrame`.

If you do call `expandAll()` / `collapseAll()` separately right after
`setGroupOn`, those calls automatically defer to the same mechanism so
they target the new groups rather than the stale snapshot. See issue #335.

```ts
setGroupOn(fn: (row: any) => any | undefined, expanded: DefaultExpandedValue): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `fn` | <code>(row: any) =&gt; any &#124; undefined</code> | The groupOn function or undefined to disable |
| `expanded` | <code><a href="/grid/plugins/grouping-rows/types/defaultexpandedvalue/">DefaultExpandedValue</a></code> | Optional initial expansion for the new grouping:
  - `true`: expand all new groups
  - `false`: collapse all new groups
  - `string` / `string[]`: expand specific group key(s)
  - `number`: expand the group at this index
  When omitted, existing `expandedKeys` are preserved (legacy behavior). |

#### Example

```ts
plugin.setGroupOn((row) => row.counterparty, true);
```

***

### setGroups()

Replace auto-detected groups with an externally provided group structure.

When groups are set, the plugin switches to pre-defined mode — `groupOn`
is ignored and the plugin renders the provided group headers instead.
Row data for each group must be populated via setGroupRows.

```ts
setGroups(groups: GroupDefinition[]): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `groups` | <code><a href="/grid/plugins/grouping-rows/interfaces/groupdefinition/">GroupDefinition</a>[]</code> | Array of group definitions, or empty array to clear |

***

### getGroups()

Get the current pre-defined group structure.

Returns the groups set via setGroups, received from `datasource:data`,
or the `groups` config array. Returns an empty array when using `groupOn`-based grouping.

```ts
getGroups(): GroupDefinition[]
```

#### Returns

`GroupDefinition[]` - Current group definitions

***

### setGroupRows()

Populate row data for an expanded group.

Call this in response to a `group-expand` event after fetching rows
from the server. The plugin will re-render to show the rows.

When `ServerSidePlugin` is loaded, this is handled automatically via
`datasource:children` events — you only need this for the imperative API.

```ts
setGroupRows(groupKey: string, rows: unknown[]): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `groupKey` | <code>string</code> | The group key to populate |
| `rows` | <code>unknown[]</code> | The row data for this group |

***

### setGroupLoading()

Toggle loading indicator for a group.

When loading is true, the group shows a loading spinner instead of row data.
Call with `false` after rows are loaded (also cleared by setGroupRows).

```ts
setGroupLoading(groupKey: string, loading: boolean): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `groupKey` | <code>string</code> | The group key |
| `loading` | <code>boolean</code> | Whether the group is loading |

***

### clearGroupRows()

Clear cached row data for one or all groups.

Use when the server data has changed and groups need to be re-fetched
on next expand.

```ts
clearGroupRows(groupKey: string): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `groupKey` | <code>string</code> | Specific group key to clear, or omit to clear all |

***
