# Column Reorder Plugin

> Allow users to reorder columns by drag and drop.

The Reorder plugin lets users rearrange columns by dragging and dropping column headers. Supports smooth FLIP animations, fade transitions, or instant reordering depending on your preference.

## Installation

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

## Basic Usage

Just enable the feature and columns become draggable. Users can grab any column header and drop it in a new position. Visual feedback and smooth animations are handled automatically.

#### 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: 'department', header: 'Department' },
  ],
  features: {
    reorderColumns: {
      animation: 'flip',
      animationDuration: 200,
    },
  },
};

// Listen for column moves
grid.on('column-move', ({ field, fromIndex, toIndex }) => {
  console.log(`Moved ${field} from ${fromIndex} to ${toIndex}`);
});
```

#### React

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

function MyGrid({ data }) {
  return (
    <DataGrid
      rows={data}
      columns={[
        { field: 'id', header: 'ID' },
        { field: 'name', header: 'Name' },
        { field: 'email', header: 'Email' },
        { field: 'department', header: 'Department' },
      ]}
      reorderColumns={{ animation: 'flip' }}
      onColumnMove={(e) => console.log(`Moved ${e.detail.field} from ${e.detail.fromIndex} to ${e.detail.toIndex}`)}
    />
  );
}
```

#### Vue

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

const data = [
  { id: 1, name: 'Alice', email: 'alice@example.com', department: 'Engineering' },
  { id: 2, name: 'Bob', email: 'bob@example.com', department: 'Marketing' },
];
</script>

<template>
  <TbwGrid :rows="data" :reorder-columns="{ animation: 'flip' }" @column-move="(e) => console.log('Column moved:', e.detail)">
    <TbwGridColumn field="id" header="ID" />
    <TbwGridColumn field="name" header="Name" />
    <TbwGridColumn field="email" header="Email" />
    <TbwGridColumn field="department" header="Department" />
  </TbwGrid>
</template>
```

#### Angular

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

@Component({
  selector: 'app-my-grid',
  imports: [Grid, GridReorderColumnsDirective],
  template: `
    <tbw-grid
      [rows]="rows"
      [columns]="columns"
      [reorderColumns]="{ animation: 'flip' }"
      (column-move)="onColumnMove($event)"
      style="height: 400px; display: block;">
    </tbw-grid>
  `,
})
export class MyGridComponent {
  rows = [...];

  columns: ColumnConfig[] = [
    { field: 'id', header: 'ID' },
    { field: 'name', header: 'Name' },
    { field: 'email', header: 'Email' },
    { field: 'department', header: 'Department' },
  ];

  onColumnMove(e: CustomEvent) {
    console.log(`Moved ${e.detail.field} from ${e.detail.fromIndex} to ${e.detail.toIndex}`);
  }
}
```

## Demos

### Default Reorder

```ts
// ReorderDefaultDemo.astro
import '@toolbox-web/grid';
import { queryGrid } from '@toolbox-web/grid';
import '@toolbox-web/grid/features/reorder-columns';

const container = document.getElementById('reorder-default-demo');
if (container) {
  const sampleData = [
    { id: 1, name: 'Alice', department: 'Engineering', email: 'alice@example.com', salary: 95000 },
    { id: 2, name: 'Bob', department: 'Marketing', email: 'bob@example.com', salary: 75000 },
    { id: 3, name: 'Carol', department: 'Engineering', email: 'carol@example.com', salary: 105000 },
  ];
  const columns = [
    { field: 'id', header: 'ID', type: 'number' },
    { field: 'name', header: 'Name' },
    { field: 'department', header: 'Department' },
    { field: 'email', header: 'Email' },
    { field: 'salary', header: 'Salary', type: 'number' },
  ];

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

  function rebuild(animation = 'flip', animationDuration = 200) {
    grid.gridConfig = {
      columns,
      features: { reorderColumns: { animation, animationDuration } as any },
    };
    grid.rows = sampleData;
  }

  rebuild();

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

Drag column headers to reorder them. Use the controls below to experiment with different animation types and durations.

## Configuration Options

See [`ReorderConfig`](./interfaces/reorderconfig/) for the full list of options and defaults.

### Animation Examples

```ts
// No animation - instant reorder
features: { reorderColumns: { animation: false } }

// FLIP animation with custom duration
features: { reorderColumns: { animation: 'flip', animationDuration: 300 } }

// View Transitions API fade effect
features: { reorderColumns: { animation: 'fade' } }
```

### Locking Individual Columns

Set `lockPosition: true` on any column to prevent users from dragging it — both in the
header row and in the [Column Visibility](../visibility/) panel. Programmatic reorder
via `grid.setColumnOrder()` still works.

```ts
columns: [
  { field: 'id', header: 'ID', lockPosition: true }, // cannot be moved
  { field: 'name', header: 'Name' },
  { field: 'email', header: 'Email' },
]
```

## Events

```ts
// ReorderColumnsEventsDemo.astro
import '@toolbox-web/grid';
import { queryGrid } from '@toolbox-web/grid';
import '@toolbox-web/grid/features/reorder-columns';

const container = document.getElementById('reorder-columns-events-demo');
if (container) {
  const grid = queryGrid('tbw-grid', container);

  grid.gridConfig = {
    columns: [
      { field: 'name', header: 'Name' },
      { field: 'department', header: 'Department' },
      { field: 'role', header: 'Role' },
      { field: 'salary', header: 'Salary', type: 'number', align: 'right' },
    ],
    features: { reorderColumns: true },
  };

  grid.rows = [
    { name: 'Alice Johnson', department: 'Engineering', role: 'Lead', salary: 95000 },
    { name: 'Bob Smith', department: 'Marketing', role: 'Manager', salary: 75000 },
    { name: 'Carol White', department: 'Sales', role: 'Rep', salary: 68000 },
    { name: 'David Brown', department: 'Engineering', role: 'Senior', salary: 92000 },
    { name: 'Eve Davis', department: 'HR', role: 'Specialist', salary: 65000 },
  ];

  const log = container.querySelector('#reorder-col-events-log');
  const clearBtn = container.querySelector('#clear-reorder-col-events-log');

  function addLog(type: string, detail: string) {
    if (!log) return;
    const msg = document.createElement('div');
    msg.innerHTML = `<span class="event-type">[${type}]</span> ${detail}`;
    log.insertBefore(msg, log.firstChild);
    while (log.children.length > 15) log.lastChild?.remove();
  }

  clearBtn?.addEventListener('click', () => { if (log) log.innerHTML = ''; });

  grid.on('column-move', (d) => {
    addLog('column-move', `"${d.field}" from index ${d.fromIndex} → ${d.toIndex}`);
  });
}
```

| Event         | Detail                                        | Cancelable | Description                                       |
| ------------- | --------------------------------------------- | ---------- | ------------------------------------------------- |
| `column-move` | `{ field, fromIndex, toIndex, columnOrder }` | Yes        | Fired when a column move is attempted. Call `preventDefault()` to block the move. |

## Keyboard Shortcuts

| Key       | Action                    |
| --------- | ------------------------- |
| `Alt + ←` | Move focused column left  |
| `Alt + →` | Move focused column right |

## Column Group Drag

When [Column Grouping](../grouping-columns/) is also active, group header cells become draggable.
Dragging a group header moves all columns in that fragment as a block. If a group is fragmented
(split across non-contiguous positions), each fragment can be dragged independently.

## See Also

- **[Pinned Columns](/grid/plugins/pinned-columns.md)** — Sticky columns
- **[Column Visibility](../visibility/)** — Show/hide columns
- **[Column Groups](../grouping-columns/)** — Group column headers
