# Gantt Context Menu
URL: https://pro.rv-grid.com/guides/gantt/features/context-menu/
Source: src/content/docs/guides/gantt/features/context-menu.mdx
Description: Use the built-in Gantt row, column, and timeline context menus and extend them with RevoGrid Pro menu items.
Gantt installs a Gantt-aware context menu on top of the RevoGrid Pro `ContextMenuPlugin`. It uses the existing `rowContextMenu` and `columnContextMenu` grid properties instead of introducing a separate menu renderer.

The Gantt plugin prepends generated menu items and preserves application-provided menu items.

The integration is enabled by default for every `GanttPlugin` instance. Set `gantt.contextMenu` to `false` to opt out of generated Gantt row, column, and timeline menu items while leaving application-provided RevoGrid context menu configuration untouched:

```ts
grid.gantt = {
  ...project,
  contextMenu: false,
};
```

You can also disable one generated surface at a time:

```ts
grid.gantt = {
  ...project,
  contextMenu: {
    row: false,
    column: true,
  },
};
```

## Row Menu

Right-click a task-table row or task bar to open task actions:

- `Add`
- `Convert to milestone`
- `Indent`
- `Outdent`
- `Delete`

The default actions dispatch Gantt events:

```ts
grid.addEventListener('gantt-task-create', (event) => {
  const { parentId, insertAfterTaskId } = event.detail ?? {};
});

grid.addEventListener('gantt-task-indent', (event) => {
  indentTask(event.detail?.taskId);
});

grid.addEventListener('gantt-task-outdent', (event) => {
  outdentTask(event.detail?.taskId);
});

grid.addEventListener('gantt-task-delete', (event) => {
  deleteTask(event.detail?.taskId);
});
```

`Add` uses the focused row or clicked task bar as the insertion anchor. When the anchor has descendants, Gantt inserts the new sibling after the full descendant block so the task tree stays valid. Context-menu creation names the row `New task 1`, `New task 2`, and so on, and does not open the task editor.

The generated row menu is suppressed on the pinned add-task draft row because that row is owned by the internal `GanttAddTaskRowPlugin` and is not a task entity. If you disable Gantt context-menu integration and provide your own `rowContextMenu`, use `isGanttAddTaskRow()` or the draft cell marker to skip that row. See [Add Task Row](/guides/gantt/interaction/add-task-row/).

## Column Menu

Right-click main grid headers to open column actions:

- `Search`
- `Sort ascending`
- `Sort descending`
- `Filter column`

Visibility follows the active column:

- Search appears only when the task/name column has `filter` configured.
- Sort actions appear only when the target column has `sortable: true`.
- Filter appears only when the target column has `filter` configured.
- If contextual filtering leaves no visible items, the menu is not opened.

Search dispatches `gantt-search` with the query entered in the prompt. Sort uses RevoGrid column sorting. Filter opens the existing filter header control for the column.

## Timeline Menu

Right-click the pinned Gantt timeline/canvas header to open timeline actions:

- `Zoom in`
- `Zoom out`

Zoom actions are hidden when the current zoom level cannot move further in that direction. If neither direction is available, the menu is not opened.

## Read-Only And Locked State

The row menu respects packaged Gantt protection state:

- `gantt.readOnly: true` hides mutating row actions.
- Selected locked tasks hide hierarchy actions.
- Mutation services still guard direct calls, so blocked task operations return `readonly-task`.

## Extending The Menu

Use the normal RevoGrid Pro menu config surfaces. Gantt prepends its generated items and keeps your custom items.

```ts
grid.rowContextMenu = {
  items: [
    {
      name: 'Open task details',
      action: (_event, focused, _range, _close, context) => {
        const task = context?.providers.data.getModel?.(focused?.y ?? 0, 'rgRow');
        openTaskDetails(task?.id);
      },
    },
  ],
};
```

## Customization

Gantt context menu customization has two layers:

- `gantt.contextMenu` controls the generated Gantt row, column, and timeline actions.
- `rowContextMenu` and `columnContextMenu` hold application menu config that should remain in the grid even when generated Gantt actions are disabled.

Use `gantt.contextMenu = false` when the application should fully own the menu:

```ts
grid.rowContextMenu = {
  items: [
    {
      name: 'Open planning panel',
      action: (_event, focused, _range, close, context) => {
        const task = context?.providers.data.getModel?.(focused?.y ?? 0, 'rgRow');
        openPlanningPanel(task?.id);
        close();
      },
    },
  ],
};

grid.gantt = {
  ...project,
  contextMenu: false,
};
```

Use partial opt-out when you want custom row actions but still want generated column and timeline actions:

```ts
grid.rowContextMenu = {
  items: [
    {
      name: 'Assign owner',
      action: (_event, focused, _range, close, context) => {
        const task = context?.providers.data.getModel?.(focused?.y ?? 0, 'rgRow');
        openOwnerPicker(task?.id);
        close();
      },
    },
  ],
};

grid.gantt = {
  ...project,
  contextMenu: {
    row: false,
    column: true,
  },
};
```

Keep `gantt.contextMenu` enabled when you want to append app actions after the default Gantt actions:

```ts
grid.rowContextMenu = {
  items: [
    {
      name: 'Show audit log',
      action: (_event, focused, _range, close, context) => {
        const task = context?.providers.data.getModel?.(focused?.y ?? 0, 'rgRow');
        showTaskAuditLog(task?.id);
        close();
      },
    },
  ],
};

grid.gantt = project;
```

You can also use a `resolve` function on the base RevoGrid menu config. Gantt preserves the resolver and passes the composed config through the context menu plugin:

```ts
grid.rowContextMenu = {
  items: [
    { name: 'Open task details', action: openTaskDetailsFromMenu },
  ],
  resolve(context) {
    return context.target === 'row' ? undefined : null;
  },
};
```

The packaged task editor item is controlled separately:

```ts
grid.ganttTaskEditorDialog = {
  contextMenu: false,
};
```

Set `ganttTaskEditorDialog.contextMenu` to `true` when `gantt.contextMenu` is disabled but the application still wants the default **Edit...** item. Leave it unset to follow `gantt.contextMenu`.

For packaged extension items, use the built-in Gantt context-menu helpers:

```ts
import { createGanttContextMenuExtensionItems } from '@revolist/revogrid-enterprise';

const customItems = createGanttContextMenuExtensionItems({
  duplicate: {
    copyName: (task) => `${task.name ?? 'Task'} copy`,
  },
  assignResource: {
    onAssignResource: (context) => {
      if (context.taskId) {
        openResourcePicker(context.taskId);
      }
    },
  },
  delete: false,
  export: { name: 'Export task rows' },
});

grid.rowContextMenu = {
  ...grid.rowContextMenu,
  items: [...(grid.rowContextMenu?.items ?? []), ...customItems],
};
```

## Legacy Context Menu

Row actions use `grid.rowContextMenu`. Column actions use `grid.columnContextMenu`.

Generated Gantt items are removed and rebuilt when the Gantt context menu tool refreshes, so application-provided items are not duplicated.

The packaged task editor follows `gantt.contextMenu` by default for its **Edit...** row-menu item. Set `ganttTaskEditorDialog.contextMenu` to `false` to disable only the editor item, or `true` to keep the editor item when generated Gantt menus are disabled.
