---
product: "AG Studio"
title: "Context Menu"
description: "Show your own menu items when a user right-clicks in the dashboard."
framework: vue
version: "3.1.0"
related:
    - title: "Popups"
      url: "https://www.ag-grid.com/studio/vue/popups/"
    - title: "Drill Through"
      url: "https://www.ag-grid.com/studio/vue/drill-through/"
    - title: "Undo & Redo"
      url: "https://www.ag-grid.com/studio/vue/undo-redo/"
    - title: "Exporting"
      url: "https://www.ag-grid.com/studio/vue/exporting/"
llms: "https://www.ag-grid.com/studio/llms.txt"
---

# Context Menu

When the user right-clicks anywhere in the dashboard canvas, Studio calls the `getContextMenuItems` callback. If the callback returns menu items, Studio opens a context menu showing them. If it returns none, no menu opens. Studio has no menu items of its own: you supply every item through the callback.

Right-click a country in the grid or a column in the chart below, then open the **Copy** sub menu. The **Show** item logs a message to the browser console, and the **Copy** items copy to the clipboard. The example returns no items for a right-click on the empty canvas, so no menu opens there.

#### Context Menu

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgStudio } from "ag-studio-vue3";
import {
  AgContextMenuItem,
  AgDataEngine,
  AgDataSourcesDefinition,
  AgFilterDefinition,
  AgGetContextMenuParams,
  AgLogicalDefinition,
  AgReportHistory,
  AgReportState,
  AgStudioApi,
  AgStudioApiReadyEvent,
  AgStudioMode,
  AgStudioProperties,
  enableStudioDevValidations,
} from "ag-studio";

if (process.env.NODE_ENV !== "production") {
  // Enable extended validations only for development
  enableStudioDevValidations();
}

function isLogicalDefinition(
  filter: AgFilterDefinition,
): filter is AgLogicalDefinition {
  return (filter as AgLogicalDefinition).conditions != null;
}

function getFilterValues(filter: AgFilterDefinition): string[] {
  if (isLogicalDefinition(filter)) {
    return filter.conditions.flatMap(getFilterValues);
  }
  // A grid total row has no value for the grouping fields, so skip empty values.
  return filter.value == null ? [] : [String(filter.value)];
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div style="display: flex; flex-direction: column; height: 100%">
      <ag-studio
        style="width: 100%; height: 100%;"
        class="my-studio-container"
        @api-ready="onApiReady"
        :initialState="initialState"
        :mode="mode"
        :getContextMenuItems="getContextMenuItems"
        :data="data"></ag-studio>
      </div>
        </div>
    `,
  components: {
    "ag-studio": AgStudio,
  },
  setup(props) {
    const studioApi = shallowRef<AgStudioApi | null>(null);
    const initialState = ref<AgReportState | AgReportHistory>({
      pages: [
        {
          id: "summary",
          widgets: {
            "medals-by-country": {
              type: "grid",
              dataMapping: {
                cols: [
                  {
                    id: "medals.country",
                  },
                  {
                    id: "medals.gold",
                    aggregation: "sum",
                  },
                  {
                    id: "medals.silver",
                    aggregation: "sum",
                  },
                  {
                    id: "medals.bronze",
                    aggregation: "sum",
                  },
                  {
                    id: "medals.total",
                    aggregation: "sum",
                  },
                ],
              },
              format: {
                title: {
                  enabled: true,
                  text: "Medals by Country",
                },
              },
              sort: [
                {
                  field: {
                    id: "medals.total",
                    aggregation: "sum",
                  },
                  direction: "desc",
                },
              ],
            },
            "medals-by-sport": {
              type: "column-chart-grouped",
              dataMapping: {
                categoryKey: [
                  {
                    id: "medals.sport",
                  },
                ],
                valueKey: [
                  {
                    id: "medals.total",
                    aggregation: "sum",
                  },
                ],
              },
              format: {
                title: {
                  enabled: true,
                  text: "Medals by Sport",
                },
              },
              sort: [
                {
                  field: {
                    id: "medals.total",
                    aggregation: "sum",
                  },
                  direction: "desc",
                },
              ],
            },
          },
          filter: {
            page: [],
            widget: {
              "medals-by-sport": [
                {
                  field: {
                    id: "medals.total",
                  },
                  view: {
                    expanded: true,
                    viewTypeId: "rank",
                  },
                  model: {
                    operator: "rank",
                    value: [10, null],
                  },
                },
              ],
            },
          },
          widgetLayout: {
            "medals-by-country": {
              xTrack: 0,
              yTrack: 0,
              xSpan: 24,
              ySpan: 16,
            },
            "medals-by-sport": {
              xTrack: 0,
              yTrack: 16,
              xSpan: 24,
              ySpan: 16,
            },
          },
        },
      ],
      selectedPageId: "summary",
      panels: {
        filters: {
          collapsed: true,
        },
        edit: {
          collapsed: true,
        },
        data: {
          collapsed: true,
        },
      },
    });
    const mode = ref<AgStudioMode>("edit");
    const data = ref<AgDataSourcesDefinition | AgDataEngine>(null);

    const onApiReady = (params: AgStudioApiReadyEvent) => {
      studioApi.value = params.api;

      const toStudioData = (data) => ({ sources: [{ id: "medals", data }] });

      fetch("https://www.ag-grid.com/studio/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((respData) => (data.value = toStudioData(respData)));
    };
    const getContextMenuItems: (
      params: AgGetContextMenuParams,
    ) => AgContextMenuItem[] = (params: AgGetContextMenuParams) => {
      const { filter, widgetId, widgetType } = params;
      // Offer no items on the empty canvas, so no menu opens there.
      if (widgetId == null) {
        return [];
      }
      const values = filter ? getFilterValues(filter) : [];
      const value = values.length > 0 ? values.join(" / ") : "Widget";
      return [
        {
          name: `Show ${value}`,
          action: () => console.log(`Show: ${value}`),
        },
        "separator",
        {
          name: "Copy",
          subMenu: [
            {
              name: "Copy Value",
              shortcut: "Ctrl+C",
              action: () => navigator.clipboard.writeText(value),
            },
            {
              name: "Copy Widget Type",
              action: () => navigator.clipboard.writeText(widgetType ?? ""),
            },
          ],
        },
      ];
    };

    return {
      studioApi,
      initialState,
      mode,
      getContextMenuItems,
      data,
      onApiReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Context Menu](https://www.ag-grid.com/studio/examples/context-menu/context-menu/vue3/)

## Providing Menu Items

Studio calls `getContextMenuItems` once for each right-click in the dashboard canvas, and opens a menu showing the items it returns. Return `'separator'` in the list to draw a divider between items:

```ts
<ag-studio
    :getContextMenuItems="getContextMenuItems"
    /* other studio properties ... */>
</ag-studio>

this.getContextMenuItems = (params) => [
    { name: 'Show Details', action: () => showDetails(params.filter) },
    'separator',
    { name: 'Export', disabled: true },
];
```

With no callback, Studio opens no menu of its own and the browser's own context menu appears instead. When the callback returns an empty array, no menu opens and the browser's context menu stays hidden. To show a menu only for some right-clicks, for example only on grid rows, check the params and return an empty array for the rest.

The callback is read again on every right-click, so you can replace it at runtime with `api.setProperty('getContextMenuItems', newCallback)`.

## Using the Callback Params

The callback is called for a right-click anywhere in the dashboard canvas, including on the empty space between widgets. Where the user right-clicks decides which params are set:

- On the empty canvas, `widgetId`, `widgetType`, `filter` and `getActiveFiltersForWidget` are not set.
- On any widget, `widgetId` and `widgetType` identify the widget, and `getActiveFiltersForWidget` is set.
- On a grid or pivot grid cell, `filter` also holds the values of the clicked row, and `widgetContext` holds the grid's context menu params. `filter` is not set for a row that is still loading.
- On a chart or gauge, `widgetContext` also holds the chart's context menu params. `filter` is set only on a chart series node, such as a column, bar or point, and not on treemap or sunburst charts.

A [Custom Widget](https://www.ag-grid.com/studio/vue/custom-widgets/) can pass the same extra information for a right-click inside it by calling `api.showContextMenu({ event, filter })` from its widget API, passing the mouse event and the filter that describes what was clicked. See [Custom Widgets - Context Menu](https://www.ag-grid.com/studio/vue/custom-widgets/#context-menu) for an example.

When set, the params hold:

- `filter`: the field values of what was right-clicked.
- `getActiveFiltersForWidget()`: returns the filters currently constraining the widget's data, split into `page`, `widget` and `crossFilter`, with selections the widget shows only as a highlight in `crossFilterHighlight` so you can choose whether to apply them. Each is omitted when no filter of that kind applies. Rank conditions, which `widget` may hold, are not supported in a drill-through filter.
- `widgetContext`: any additional context from the widget. Its shape depends on `widgetType`.
- `api` and `context`: the Studio API and your application context.

The example uses the values in `filter` to label its first item, and checks `widgetId` to offer no items on the empty canvas. [Drill Through](https://www.ag-grid.com/studio/vue/drill-through/) is built on the same callback, using `filter` to open a detail view of the clicked row.

## Sub Menus

Set `subMenu` on an item to an array of items, and the item opens them as a sub menu when hovered. Sub menus can nest:

```
{
    name: 'Copy',
    subMenu: [
        { name: 'Copy Value', action: () => copyValue() },
        { name: 'Copy Widget Type', action: () => copyWidgetType() },
    ],
}
```

## Restoring Focus

When a keyboard user closes the menu and nothing else has focus, Studio returns focus to the widget. If an action opens its own popup, call `restoreFocusOnClose` from the params when that popup closes, so focus goes back to the widget. The [Drill Through](https://www.ag-grid.com/studio/vue/drill-through/) popup example does this.

## Showing the Browser Context Menu

A right-click with `⌃ Ctrl`/`⌘ Cmd` held opens the browser's own context menu instead of the Studio menu, for example to reach the browser's developer tools. This applies everywhere in the dashboard, including grids, charts, gauges and custom widgets. A menu opened from the keyboard, with the context menu key or `⇧ Shift` + `F10`, is not affected.

To always open the Studio menu, even with `⌃ Ctrl`/`⌘ Cmd` held, set `allowContextMenuWithControlKey` to `true`:

```ts
<ag-studio
    :allowContextMenuWithControlKey="allowContextMenuWithControlKey"
    /* other studio properties ... */>
</ag-studio>

this.allowContextMenuWithControlKey = true;
```

On macOS, `⌃ Ctrl` + click is also the system's own secondary click. Users who right-click that way see the browser's context menu by default, and setting `allowContextMenuWithControlKey` to `true` opens the Studio menu for them instead.

The property can be changed at runtime with `api.setProperty('allowContextMenuWithControlKey', true)`.

## Context Menu API

### Properties

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `getContextMenuItems` | `Function` |  | Called when the user right-clicks in the dashboard canvas. Studio opens a context menu showing the returned items. Return an empty array to show no menu. When this property is not set, Studio shows no menu of its own and the browser's context menu appears. A right-click with the Ctrl key (Cmd on macOS) held also shows the browser's context menu, unless `allowContextMenuWithControlKey` is `true`. |
| `allowContextMenuWithControlKey` | `boolean` | `false` | Set to `true` to open the Studio context menu when the user right-clicks with the Ctrl key (Cmd on macOS) held. By default, a right-click with Ctrl or Cmd held shows the browser's own context menu instead, everywhere in the dashboard including grids and charts. |

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `popupParent` | [`HTMLElement \| null`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) |  | DOM element to use as the popup parent for Studio popups (context menus, etc.). |

### Menu Items

Properties available on the `AgContextMenuItemDefinition` interface.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` |  | Name of the menu item. |
| `disabled` | `boolean` |  | Set to `true` to display the menu item as disabled. |
| `shortcut` | `string` |  | Shortcut text displayed inside menu item. Setting this doesn’t actually create a keyboard shortcut binding. |
| `action` | `Function` |  | Function that gets executed when item is chosen. |
| `checked` | `boolean` |  | Set to true to provide a check beside the option. |
| `icon` | `string \| Element` |  | The icon to display, either a DOM element or HTML string. |
| `cssClasses` | `string[]` |  | CSS classes to apply to the menu item. |
| `tooltip` | `string` |  | Tooltip text to be displayed for the menu item. |
| `subMenu` | `AgContextMenuItem[]` |  | If this item is a sub menu, contains a list of menu item definitions. |

### Callback Params

Properties available on the `AgGetContextMenuParams&lt;TWidgetContext = any, TCommon = any&gt;` interface.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `widgetId` | `string` |  | ID of the widget that was right-clicked, or `undefined` if on the empty canvas. |
| `widgetType` | `string` |  | Type of the widget that was right-clicked, for example `grid` or `column-chart-grouped`, or `undefined` if on the empty canvas. |
| `widgetContext` | `TWidgetContext` |  | Context from the widget, e.g. AG Charts context menu event params for chart widgets. The shape depends on the widget type. |
| `filter` | `AgFilterDefinition` |  | The field values of what was right-clicked, such as the grouping values of a grid row or the category of a chart series node. Not set when the right-click is not on a row or data point. |
| `getActiveFiltersForWidget` | `Function` |  | Returns the page, widget and cross filters currently constraining the data of the widget that was right-clicked, with selections the widget only highlights reported separately so you can choose whether to apply them. Set when `widgetId` is set. The returned filters can be combined and supplied as a drill-through filter to another report. Rank conditions, which `widget` may contain, are not supported in a drill-through filter. |
| `event` | `MouseEvent \| Touch` |  | The mouse or touch event for the right-click. |
| `restoreFocusOnClose` | `Function` |  | Returns keyboard focus to the widget. Call this when a popup opened from a menu item action is closed. |
| `context` | `TCommon` |  | Application context as set on `context` Studio property. |
| `api` | [`AgStudioApi`](https://www.ag-grid.com/studio/vue/studio-api/) |  | Studio API. |
