# Column Visibility Plugin

> Allow users to toggle column visibility via a panel.

The Visibility plugin gives users control over which columns are displayed. Hide less important columns by default, let users toggle them via a column chooser UI, or programmatically show/hide columns based on user preferences or screen size.

> 💡 **Optional Enhancement:** When [ReorderPlugin](/grid/plugins/reorder-columns.md) is also loaded, columns in the visibility panel become draggable for reordering. When [Column Grouping](/grid/plugins/grouping-columns.md) is also active, the panel shows columns in their actual display order, and fragmented groups (split across non-contiguous positions) appear as separate panel sections. Group headers in the panel are draggable and move only the columns in that fragment.

## Installation

```ts
import '@toolbox-web/grid/features/visibility';
```

## Basic Usage

Set `hidden: true` on columns you want hidden by default. The feature provides a UI and API for toggling column visibility at runtime.

#### TypeScript

```ts
import { queryGrid } from '@toolbox-web/grid';

const grid = queryGrid('tbw-grid');
grid.gridConfig = {
  columns: [
    { field: 'id', header: 'ID' },
    { field: 'name', header: 'Name' },
    { field: 'email', header: 'Email' },
    { field: 'phone', header: 'Phone', hidden: true }, // Hidden by default
    { field: 'address', header: 'Address', hidden: true },
  ],
  features: { visibility: true },
};

// Toggle visibility programmatically
const plugin = grid.getPluginByName('visibility');
plugin.showColumn('phone');
plugin.hideColumn('email');
```

#### React

```tsx
import '@toolbox-web/grid-react/features/visibility';
import { DataGrid } from '@toolbox-web/grid-react';

function ConfigurableGrid({ data }) {
  return (
    <DataGrid
      rows={data}
      columns={[
        { field: 'id', header: 'ID' },
        { field: 'name', header: 'Name' },
        { field: 'email', header: 'Email' },
        { field: 'phone', header: 'Phone', hidden: true },
      ]}
      visibility
    />
  );
}
```

#### Vue

```html
<script setup>
import '@toolbox-web/grid-vue/features/visibility';
import { TbwGrid, TbwGridColumn } from '@toolbox-web/grid-vue';

const data = [
  { id: 1, name: 'Alice', email: 'alice@example.com', phone: '555-1234' },
  { id: 2, name: 'Bob', email: 'bob@example.com', phone: '555-5678' },
];
</script>

<template>
  <TbwGrid :rows="data" visibility>
    <TbwGridColumn field="id" header="ID" />
    <TbwGridColumn field="name" header="Name" />
    <TbwGridColumn field="email" header="Email" />
    <TbwGridColumn field="phone" header="Phone" :hidden="true" />
  </TbwGrid>
</template>
```

#### Angular

```typescript
// Feature import - enables the [visibility] input
import { GridVisibilityDirective } from '@toolbox-web/grid-angular/features/visibility';
import { Component } from '@angular/core';
import { Grid } from '@toolbox-web/grid-angular';
import type { ColumnConfig } from '@toolbox-web/grid';

@Component({
  selector: 'app-configurable-grid',
  imports: [Grid, GridVisibilityDirective],
  template: `
    <tbw-grid
      [rows]="rows"
      [columns]="columns"
      [visibility]="true"
      style="height: 400px; display: block;">
    </tbw-grid>
  `,
})
export class ConfigurableGridComponent {
  rows = [];

  columns: ColumnConfig[] = [
    { field: 'id', header: 'ID' },
    { field: 'name', header: 'Name' },
    { field: 'email', header: 'Email' },
    { field: 'phone', header: 'Phone', hidden: true },
  ];
}
```

## Demo

Toggle the controls to explore hidden columns and locked columns. Click the column visibility icon in the header to open the panel.

```ts
// VisibilityDefaultDemo.astro
import '@toolbox-web/grid';
import { queryGrid } from '@toolbox-web/grid';
import '@toolbox-web/grid/features/visibility';

const container = document.getElementById('visibility-default-demo');
if (container) {
  const allColumns = [
    { field: 'id', header: 'ID', type: 'number' as const },
    { field: 'name', header: 'Name' },
    { field: 'email', header: 'Email' },
    { field: 'department', header: 'Department' },
    { field: 'salary', header: 'Salary', type: 'number' as const },
  ];

  const sampleData = [
    { id: 1, name: 'Alice', email: 'alice@example.com', department: 'Engineering', salary: 95000 },
    { id: 2, name: 'Bob', email: 'bob@example.com', department: 'Marketing', salary: 75000 },
    { id: 3, name: 'Carol', email: 'carol@example.com', department: 'Engineering', salary: 105000 },
    { id: 4, name: 'David', email: 'david@example.com', department: 'Sales', salary: 65000 },
    { id: 5, name: 'Eve', email: 'eve@example.com', department: 'HR', salary: 70000 },
  ];

  const grid = queryGrid('tbw-grid', container)!;

  function rebuild(opts: Record<string, unknown>) {
    const hiddenArr = (opts.hiddenColumns as string[]) ?? ['email'];
    const lockedArr = (opts.lockedColumns as string[]) ?? ['id'];

    grid.gridConfig = {
      columns: allColumns.map((col) => ({
        ...col,
        hidden: hiddenArr.includes(col.field),
        lockVisible: lockedArr.includes(col.field),
      })),
      features: { visibility: {} },
    };
    grid.rows = sampleData;
  }

  rebuild({ hiddenColumns: ['email'], lockedColumns: ['id'] });

  container.addEventListener('control-change', ((e: CustomEvent) => {
    rebuild(e.detail.allValues);
  }) as EventListener);
}
```

## Configuration Options

See [`VisibilityConfig`](./Interfaces/VisibilityConfig/) for the full list of options and defaults.

## Programmatic API

```ts
const plugin = grid.getPluginByName('visibility');

// Panel control
plugin.show();                          // Open the visibility panel
plugin.hide();                          // Close the panel
plugin.toggle();                        // Toggle panel open/closed
const open = plugin.isPanelVisible();   // Check if panel is open

// Column visibility
plugin.hideColumn('email');
plugin.showColumn('email');
plugin.toggleColumn('email');
plugin.showAll();                       // Show all hidden columns
plugin.setColumnVisible('email', false); // Set visibility directly
const isVis = plugin.isColumnVisible('email');

// Query state
const hidden = plugin.getHiddenColumns();   // List of hidden field names
const visible = plugin.getVisibleColumns(); // List of visible field names
const all = plugin.getAllColumns();          // Full column metadata with visibility
```

## Styling

The visibility panel supports CSS custom properties for theming. Override these on `tbw-grid` or a parent container:

### CSS Custom Properties

| Property | Default | Description |
| --- | --- | --- |
| `--tbw-visibility-hover` | `var(--tbw-color-row-hover)` | Row hover background |
| `--tbw-panel-padding` | `var(--tbw-spacing-md, 0.5rem)` | Panel content padding |
| `--tbw-panel-gap` | `var(--tbw-spacing-md, 0.5rem)` | Gap between items |
| `--tbw-menu-item-padding` | `0.375rem 0.25rem` | Row padding |
| `--tbw-font-size-sm` | `0.8125rem` | Label font size |
| `--tbw-font-size-2xs` | `0.625rem` | Drag handle size |
| `--tbw-color-fg-muted` | `#888` | Muted text (locked items) |
| `--tbw-border-radius` | `0.25em` | Row border radius |

### Example

```css
tbw-grid {
  /* Custom visibility panel styling */
  --tbw-visibility-hover: #e8f4fd;
  --tbw-panel-padding: 1rem;
  --tbw-panel-gap: 0.75rem;
}
```

### CSS Classes

The visibility panel uses these class names for advanced customization:

| Class | Element |
| --- | --- |
| `.tbw-visibility-content` | Panel container |
| `.tbw-visibility-list` | Scrollable column list |
| `.tbw-visibility-row` | Individual column row |
| `.tbw-visibility-row.locked` | Non-toggleable column |
| `.tbw-visibility-row.reorderable` | Can be dragged (with ReorderPlugin) |
| `.tbw-visibility-row.dragging` | Currently being dragged |
| `.tbw-visibility-row--grouped` | Column row inside a group section |
| `.tbw-visibility-group-header` | Group header row (with checkbox) |
| `.tbw-visibility-group-header.reorderable` | Draggable group header |
| `.tbw-visibility-handle` | Drag handle element |
| `.tbw-visibility-label` | Checkbox + text wrapper |

## See Also

- **[Column Reorder](../reorder-columns/)** — Drag-to-reorder columns
- **[Pinned Columns](/grid/plugins/pinned-columns.md)** — Sticky columns
