# Action Column Menus

Canonical page: https://www.adaptabletools.com/docs/handbook-action-column-menu

- A `menu` Control puts a whole set of row actions behind one compact button in a cell
- Run-time users get every action they need, right on the row, without a cluttered Grid
- Menus can contain any Action Column Control (Buttons, Commands, Forms, even other Menus)
- Choose a **Dropdown** (one button that opens a list) or a **Split Button** (a main action plus a list of extras)

Rows often need more actions than a cell has room for: edit, clone, delete, approve, reprice, escalate...

Showing each as its own button quickly makes the Grid cramped and noisy.

The Action Column `menu` Control solves this by grouping those actions behind a single button in the cell.

Run-time users click it to see every available action for that row, and pick the one they want.

- **Keeps the Grid clean**: one small button per row instead of a row of icons
- **Scales with your workflow**: add new actions to the menu without widening the Column
- **Actions stay in context**: users act on row they're looking at, with no need to select it first or look elsewhere in UI
- **Mix and match**: one menu can combine custom Buttons, built-in Commands (edit, clone, delete) and Forms
- **Organise complex actions**: nest menus inside menus to group related actions (e.g. an "Export" sub-menu)

The items in the menu are provided in the `controls` array.

This can include any [`ActionColumnControl`](https://www.adaptabletools.com/docs/reference/actioncolumncontrol.md), including nested menus.

## Menu Styles

AdapTable offers 2 styles of Menu, set via the `variant` property.

The choice depends on whether one action is used much more often than the others:

| Style        | `variant`             | What the user sees                                                                | Best when                                                |
| ------------ | --------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------- |
| Dropdown     | `dropdown` (default)  | A single button; clicking it opens the list of actions                            | All the actions are roughly equally important            |
| Split Button | `split`               | A button that runs the main action straight away, plus a small arrow for the rest | One action is used most of the time (e.g. Edit)          |

A Split Button saves users a click on the action they use most, while still keeping the less common actions close by

### Dropdown

A Dropdown is a single labelled button in the cell. Clicking it opens a list of all the actions in `controls` and the user picks one.

It is the default style, and the natural choice when no one action stands out from the others.

### Defining a Dropdown Menu

Put the Column in `actionColumnOptions.actionColumns`.

Supply `columnId` and an optional `friendlyName`.

Set `type` to `'menu'` and give the trigger a `label`.

`dropdown` (the default) shows a single button that opens the list. Use `split` instead when one action should run with a single click.

`controls` is the menu body. Here two [Buttons](https://www.adaptabletools.com/docs/handbook-action-column-button/index.md) increment or decrement a cell.

This configures what happens when the menu item is clicked.

In our example we increment or decrement a cell.

```ts [[1, 1, "actionColumnOptions"], [1, 2, "actionColumns"], [2, 4, "columnId"], [2, 5, "friendlyName"], [3, 8, "type"], [3, 9, "label"], [4, 10, "variant"], [5, 11, "controls"],[6, 16, "onClick"],[6, 22, "onClick"]]
actionColumnOptions: {
  actionColumns: [
    {
      columnId: 'update_change',
      friendlyName: 'Change',
      controls: [
        {
          type: 'menu',
          label: 'Change',
          variant: 'dropdown',
          controls: [
            {
              type: 'button',
              label: 'Increment',
              icon: {name: 'plus'},
              onClick: (context) => bumpIssueChange(context, 1),
            },
            {
              type: 'button',
              label: 'Decrement',
              icon: {name: 'minus'},
              onClick: (context) => bumpIssueChange(context, -1),
            },
          ],
        },
      ],
    },
  ],
},
```

**Example: Action Columns: Menu**

Dropdown menu containing Buttons and Commands

- `Change` is a dropdown with increment / decrement Buttons
- `Manage` is a dropdown with `edit` and `delete` Commands

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts       [template]
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {
  ActionColumnContext,
  AdaptableOptions,
  CellUpdateRequest,
} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

const bumpIssueChange = (
  context: ActionColumnContext<WebFramework>,
  delta: number
) => {
  const cellUpdateRequest: CellUpdateRequest = {
    columnId: 'week_issue_change',
    newValue: (context.data?.week_issue_change ?? 0) + delta,
    primaryKeyValue: context.primaryKeyValue,
    rowNode: context.rowNode,
  };
  context.adaptableApi.gridApi.setCellValue(cellUpdateRequest);
};

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Action Column Menu',
  actionColumnOptions: {
    actionColumns: [
      {
        columnId: 'update_change',
        friendlyName: 'Change',
        settings: {autoWidth: true, minWidth: 110},
        controls: [
          {
            type: 'menu',
            label: 'Change',
            variant: 'dropdown',
            controls: [
              {
                type: 'button',
                label: 'Increment',
                icon: {name: 'plus'},
                onClick: context => bumpIssueChange(context, 1),
              },
              {
                type: 'button',
                label: 'Decrement',
                icon: {name: 'minus'},
                onClick: context => bumpIssueChange(context, -1),
              },
            ],
          },
        ],
      },
      {
        columnId: 'row_actions',
        friendlyName: 'Manage',
        settings: {autoWidth: true, minWidth: 110},
        controls: [
          {
            type: 'menu',
            label: 'Manage',
            variant: 'dropdown',
            controls: [
              {type: 'command', command: 'edit', label: 'Edit'},
              {
                type: 'command',
                command: 'delete',
                label: 'Delete',
                confirm: {
                  title: 'Delete row?',
                  tone: 'danger',
                },
              },
            ],
          },
        ],
      },
    ],
  },
  rowFormOptions: {
    autoHandle: true,
    disableInlineEditing: true,
    setPrimaryKeyValue: context => ({
      ...context.rowData,
      id: context.adaptableApi.gridApi.getRowCount() + 1,
    }),
  },
  initialState: {
    Theme: {CurrentTheme: 'dark'},
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          Name: 'Standard Layout',
          TableColumns: [
            'name',
            'week_issue_change',
            'update_change',
            'language',
            'license',
            'row_actions',
          ],
          ColumnPinning: {row_actions: 'right'},
          AutoSizeColumns: true,
        },
      ],
    },
    FlashingCell: {
      FlashingCellDefinitions: [
        {
          Name: 'flash-issue-change',
          Scope: {
            DataTypes: ['number'],
          },
          Rule: 'ANY_CHANGE()',
        },
      ],
    },
  },
};
```

### Split button

A Split Button is a button in 2 parts, familiar from applications like Outlook and Excel:

- The **main part** runs the most common action immediately, in one click
- A small **arrow** (chevron) beside it opens a list of the other, less frequent actions

This gives users the fastest possible route to what they do most, without hiding everything else.

To create one, set `variant` to `'split'`, provide the main action in `primary`, and put the extra actions in `controls`.

### Defining a Split Menu

`type` is `'menu'` and `variant` is `'split'`.

`primary` is the directly-clickable action — here an `edit` [Command](https://www.adaptabletools.com/docs/handbook-action-column-command/index.md).

`controls` are the extra actions opened by the chevron.

```ts [[1, 2, "type"], [1, 3, "variant"], [2, 4, "primary"], [3, 9, "controls"]]
{
  type: 'menu',
  variant: 'split',
  primary: {
    type: 'command',
    command: 'edit',
    label: 'Edit',
  },
  controls: [
    {type: 'command', command: 'clone', label: 'Clone'},
    {
      type: 'command',
      command: 'delete',
      label: 'Delete',
      confirm: {title: 'Delete row?', tone: 'danger'},
    },
  ],
}
```

**Example: Action Columns: Split Menu**

Split button with a primary Edit Command

- The main part of the Control runs `edit`
- The chevron opens `clone` and `delete`

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts       [template]
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {AdaptableOptions} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Action Column Split Menu',
  actionColumnOptions: {
    actionColumns: [
      {
        columnId: 'row_actions',
        friendlyName: 'Actions',
        settings: {autoWidth: true, minWidth: 130},
        controls: [
          {
            type: 'menu',
            variant: 'split',
            primary: {
              type: 'command',
              command: 'edit',
              label: 'Edit',
            },
            controls: [
              {type: 'command', command: 'clone', label: 'Clone'},
              {
                type: 'command',
                command: 'delete',
                label: 'Delete',
                confirm: {title: 'Delete row?', tone: 'danger'},
              },
            ],
          },
        ],
      },
    ],
  },
  rowFormOptions: {
    autoHandle: true,
    disableInlineEditing: true,
    setPrimaryKeyValue: context => ({
      ...context.rowData,
      id: context.adaptableApi.gridApi.getRowCount() + 1,
    }),
  },
  initialState: {
    Theme: {CurrentTheme: 'dark'},
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          Name: 'Standard Layout',
          TableColumns: [
            'name',
            'language',
            'license',
            'github_stars',
            'row_actions',
          ],
          ColumnPinning: {row_actions: 'right'},
          AutoSizeColumns: true,
        },
      ],
    },
  },
};
```
