---
title: "Context Menu"
enterprise: true
framework: vue
version: "14.1.0"
---

# Context Menu

The Context Menu provides context-aware interactions with the chart elements.

#### Context Menu

```ts
import { createApp, defineComponent, ref } from "vue";
import { AgCharts } from "ag-charts-vue3";
import type { AgChartOptions } from "ag-charts-types";
import {
  AnimationModule,
  BarSeriesModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrosshairModule,
  LegendModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-enterprise";

ModuleRegistry.registerModules([
  AnimationModule,
  BarSeriesModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrosshairModule,
  LegendModule,
  NumberAxisModule,
]);

const ChartExample = defineComponent({
  template: `
    <ag-charts
      :options="options"
    />
  `,
  components: {
    "ag-charts": AgCharts,
  },
  setup(props) {
    const options = ref<AgCartesianChartOptions>({
      title: {
        text: "Sweaters made",
      },
      data: [
        {
          month: "Jun",
          sweaters: 50,
          hats: 40,
        },
        {
          month: "Jul",
          sweaters: 70,
          hats: 50,
        },
        {
          month: "Aug",
          sweaters: 60,
          hats: 30,
        },
      ],
      series: [
        {
          type: "bar",
          xKey: "month",
          yKey: "sweaters",
          yName: "Sweaters Made",
        },
        {
          type: "bar",
          xKey: "month",
          yKey: "hats",
          yName: "Hats Made",
        },
      ],
    });

    return {
      options,
    };
  },
});

createApp(ChartExample).mount("#app");
```

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

In this example:

- Right clicking anywhere on the chart shows the Context Menu with the option to download the chart.
- Right clicking on a legend item will show the Context Menu with additional options to toggle series visibility.

The Context Menu is enabled by default, to disable set `contextMenu.enabled` to `false`.

```js
{
    contextMenu: {
        enabled: false,
    },
}
```

## Built-in Items

The Context Menu displays default items based on the right-clicked element.

The `contextMenu.items` array accepts special string values to conveniently reconfigure these defaults.

#### Context Menu Built-in Items

```ts
import { createApp, defineComponent, ref } from "vue";
import { AgCharts } from "ag-charts-vue3";
import type { AgChartOptions } from "ag-charts-types";
import {
  AnimationModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrosshairModule,
  LegendModule,
  LineSeriesModule,
  ModuleRegistry,
  NumberAxisModule,
  ZoomModule,
} from "ag-charts-enterprise";
import { generateCurrencyData } from "./data";
import clone from "clone";

const CUSTOM_ORDER = [
  "toggle-series-visibility",
  "toggle-other-series",
  "separator",
  "zoom-to-cursor",
  "pan-to-cursor",
  "reset-zoom",
  "separator",
  "download",
];

ModuleRegistry.registerModules([
  AnimationModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrosshairModule,
  LegendModule,
  LineSeriesModule,
  NumberAxisModule,
  ZoomModule,
]);

const ChartExample = defineComponent({
  template: `
    <div class="example-controls">
      <div class="controls-row">
        <button v-on:click="onDefaultOrder()">Default Order</button>
        <button v-on:click="onCustomOrder()">Custom Order</button>
      </div>
    </div>
    <ag-charts
      :options="options"
    />
  `,
  components: {
    "ag-charts": AgCharts,
  },
  setup(props) {
    const options = ref<AgCartesianChartOptions>({
      title: { text: "Inflation-Adjusted Currency Values (1800–2025)" },
      zoom: { enabled: true },
      contextMenu: {
        items: CUSTOM_ORDER,
      },
      data: generateCurrencyData(),
      series: [
        { type: "line", xKey: "year", yKey: "USD" },
        { type: "line", xKey: "year", yKey: "GBP" },
        { type: "line", xKey: "year", yKey: "JPY" },
      ],
      axes: {
        x: { type: "category", label: { autoRotate: false } },
      },
    });

    const onCustomOrder = () => {
      const optionsCopy = clone(options.value);

      optionsCopy.contextMenu.items = CUSTOM_ORDER;

      options.value = optionsCopy;
    };
    const onDefaultOrder = () => {
      const optionsCopy = clone(options.value);

      optionsCopy.contextMenu.items = ["defaults"];

      options.value = optionsCopy;
    };

    return {
      options,
      onCustomOrder,
      onDefaultOrder,
    };
  },
});

createApp(ChartExample).mount("#app");
```

[Live example: Context Menu Built-in Items](https://www.ag-grid.com/charts/vue3/context-menu/examples/context-menu-builtins)

```js
{
    contextMenu: {
        items: [
            'defaults', //all the default menu-items in the pre-determined order.
            'separator', // a non-interactive horizontal line.
            'toggle-series-visibility', // used for legend items.
            'toggle-other-series', // used for legend items in multi-series charts.
            'zoom-to-cursor', // used with zoom.
            'pan-to-cursor', // used with zoom.
            'reset-zoom', // used with zoom.
            'download',
        ],
    },
}
```

In this example:

- The "Custom Order" button reorders the default built-in Menu Items and adds separators.
- The "Default Order" button resets the Menu Items to the default `['defaults']` value.

These string values can be used in combination with [Custom Actions](#custom-actions).

## Custom Actions

The Context Menu's custom actions can be used to run arbitrary functions based on the element that is right-clicked.

#### Context Menu Custom Actions

```ts
import { createApp, defineComponent, ref } from "vue";
import { AgCharts } from "ag-charts-vue3";
import type { AgChartOptions } from "ag-charts-types";
import {
  AnimationModule,
  BarSeriesModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrossLinesModule,
  CrosshairModule,
  LegendModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-enterprise";
import { getData } from "./data";

ModuleRegistry.registerModules([
  AnimationModule,
  BarSeriesModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrossLinesModule,
  CrosshairModule,
  LegendModule,
  NumberAxisModule,
]);

const ChartExample = defineComponent({
  template: `
    <ag-charts
      :options="options"
    />
  `,
  components: {
    "ag-charts": AgCharts,
  },
  setup(props) {
    const options = ref<AgCartesianChartOptions<DataType>>({
      title: {
        text: "Q3 Production",
      },
      subtitle: {
        text: "Total clothing manufactured from July to September",
      },
      footnote: {
        text: "Note: This data excludes products made with manufacturing defects.",
      },
      contextMenu: {
        items: [
          "defaults",
          "separator",
          {
            showOn: "always",
            label: "Say hello",
            action: () => {
              console.log("Hello world!");
            },
          },
          "separator",
          {
            showOn: "axis",
            label: "Say hello to an axis",
            action: (ev) => {
              console.log(`Hello in axis "${ev.axisId}":"`, ev);
            },
          },
          "separator",
          {
            showOn: "caption",
            label: "Say hello in a caption",
            action: ({ captionType, text }) => {
              console.log(`Hello in a ${captionType} caption: "${text}"`);
            },
          },
          "separator",
          {
            showOn: "series-area",
            label: "Say hello in the series area",
            action: () => {
              console.log("Hello in the series area!");
            },
          },
          "separator",
          {
            showOn: "series-node",
            label: "Say hello to a node",
            action: ({ datum, yKey }) => {
              console.log(`Hello ${yKey} in ${datum.month}!`);
            },
          },
          "separator",
          {
            showOn: "legend-item",
            label: "Say hello to a legend item",
            action: ({ itemId }) => {
              console.log(`Hello ${itemId}!`);
            },
          },
          {
            showOn: "cross-line",
            label: "Say hello to a cross line",
            action: ({ value, direction, crossLineType }) => {
              console.log(`Hello ${direction}-${crossLineType} ${value}!`);
            },
          },
        ],
      },
      data: getData(),
      series: [
        {
          type: "bar",
          xKey: "month",
          yKey: "sweaters",
          yName: "Sweaters Made",
        },
        {
          type: "bar",
          xKey: "month",
          yKey: "hats",
          yName: "Hats Made",
        },
      ],
      axes: {
        y: {
          type: "number",
          crossLines: [
            {
              type: "line",
              value: 53,
              label: {
                text: "Target",
                position: "top-right",
                fontStyle: "italic",
              },
              lineDash: [2, 4],
            },
          ],
        },
      },
    });

    return {
      options,
    };
  },
});

createApp(ChartExample).mount("#app");
```

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

Use the `showOn` property to specify when a custom item should be shown:

- `always` - shown regardless of what was clicked.
- `axis` - shown when right-clicking an axis.
- `caption` - shown when right-clicking a caption (title, subtitle, footnote).
- `cross-line` - shown when right-clicking a [Cross Line's](https://www.ag-grid.com/charts/vue/axes-cross-lines/) line or fill.
- `series-area` - shown when right-clicking anywhere within the series area bounds.
- `series-node` - shown when right-clicking a datum node.
- `legend-item` - shown when right-clicking a legend item.

If there are multiple elements that match the `showOn` value, all of them are shown in the Context Menu.

```js
{
    contextMenu: {
        items: [
            'defaults',
            'separator',
            {
                showOn: 'always',
                label: 'Say hello',
                action: () => console.log('Hello world!'),
            },
            'separator',
            {
                showOn: 'axis',
                label: 'Say hello to an axis',
                action: (ev) => console.log(`Hello in axis "${ev.axisId}":"`, ev),
            },
            {
                showOn: 'series-area',
                label: 'Say hello in the series area',
                action: () => console.log('Hello in the series area!'),
            },
            'separator',
            {
                showOn: 'series-node',
                label: 'Say hello to a node',
                action: ({ datum, yKey }) => console.log(`Hello ${yKey} in ${datum.month}!`),
            },
            //...more custom actions for legend items, captions, etc.
        ],
    },
}
```

In this configuration:

- A single custom action is added for the entire chart, the captions, the axes, the series area, the series node and the legend items.
- Multiple entries per `showOn` element can be specified within the array if required.
- Right clicking on one of these areas will show these additional actions in the Context Menu.
- Clicking these extra actions will display information in the console, demonstrating that the action is aware of details about the right-clicked item.

## Sub-Menus

To add Sub-Menus to the Context Menu, simply use the `items` property recursively.

#### Context Menu Sub-Menus

```ts
import { createApp, defineComponent, ref } from "vue";
import { AgCharts } from "ag-charts-vue3";
import type { AgChartOptions } from "ag-charts-types";
import {
  AnimationModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrossLinesModule,
  CrosshairModule,
  LegendModule,
  LineSeriesModule,
  ModuleRegistry,
  NumberAxisModule,
  ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";

ModuleRegistry.registerModules([
  AnimationModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrossLinesModule,
  CrosshairModule,
  LegendModule,
  LineSeriesModule,
  NumberAxisModule,
  ZoomModule,
]);

const ChartExample = defineComponent({
  template: `
    <ag-charts
      :options="options"
    />
  `,
  components: {
    "ag-charts": AgCharts,
  },
  setup(props) {
    const options = ref<AgCartesianChartOptions<DataType>>({
      title: { text: "GDP Growth (1995–2024)" },
      subtitle: { text: "Trillions USD" },
      footnote: {
        text: "Disclaimer: This data is for illustration purposes only.",
      },
      contextMenu: {
        items: [
          "download",
          {
            showOn: "series-area",
            label: "Zoom Controls",
            items: ["zoom-to-cursor", "pan-to-cursor", "reset-zoom"],
          },
          {
            showOn: "legend-item",
            label: "Legend Controls",
            items: ["toggle-series-visibility", "toggle-other-series"],
          },
          "separator",
          {
            label: "Debug Console",
            items: [
              {
                showOn: "always",
                label: `On 'always'`,
                action: () => console.log(`On 'always' clicked.`),
              },
              {
                showOn: "axis",
                label: `On 'axis'`,
                action: (ev) =>
                  console.log(`On 'axis' clicked -`, ev.axisId, ev),
              },
              {
                showOn: "caption",
                label: `On 'caption'`,
                action: ({ captionType, text }) =>
                  console.log(`On 'caption' clicked -`, captionType, text),
              },
              {
                showOn: "series-area",
                label: `On 'series-area'`,
                action: () => console.log(`On 'series-area' clicked.`),
              },
              {
                showOn: "series-node",
                label: `On 'series-node'`,
                action: ({ datum, xKey, yKey }) =>
                  console.log(
                    `On 'series-node' clicked -`,
                    yKey,
                    datum[xKey],
                    datum[yKey],
                  ),
              },
              {
                showOn: "legend-item",
                label: `On 'legend-item'`,
                action: ({ itemId }) =>
                  console.log(`On 'legend-item' clicked -`, itemId),
              },
              {
                showOn: "cross-line",
                label: `On 'cross-line'`,
                action: ({ crossLineType, value }) =>
                  console.log(
                    `On 'cross-line' clicked -`,
                    crossLineType,
                    value,
                  ),
              },
            ],
          },
        ],
      },
      data: getData(),
      legend: { position: "bottom" },
      zoom: { enabled: true },
      series: [
        {
          type: "line",
          marker: { size: 12 },
          tooltip: { range: "exact" },
          xKey: "year",
          yKey: "USA",
        },
        {
          type: "line",
          marker: { size: 12 },
          tooltip: { range: "exact" },
          xKey: "year",
          yKey: "EU",
        },
        {
          type: "line",
          marker: { size: 12 },
          tooltip: { range: "exact" },
          xKey: "year",
          yKey: "China",
        },
        {
          type: "line",
          marker: { size: 12 },
          tooltip: { range: "exact" },
          xKey: "year",
          yKey: "India",
        },
      ],
      axes: {
        y: { type: "number", title: { text: "GDP (Trillions USD)" } },
        x: {
          type: "category",
          label: { autoRotate: false },
          crossLines: [
            {
              type: "line",
              value: 2020,
              label: {
                text: "China overtakes EU",
                position: "top",
                fontStyle: "italic",
              },
              lineDash: [2, 4],
            },
          ],
        },
      },
    });

    return {
      options,
    };
  },
});

createApp(ChartExample).mount("#app");
```

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

```js
{
    contextMenu: {
        items: [
            'download',
            {
                showOn: 'series-area',
                label: 'Zoom Controls',
                items: ['zoom-to-cursor', 'pan-to-cursor', 'reset-zoom'],
            },
            {
                showOn: 'legend-item',
                label: 'Legend Controls',
                items: ['toggle-series-visibility', 'toggle-other-series'],
            },
            'separator',
            {
                label: 'Debug Console',
                items: [
                    {
                        showOn: 'always',
                        label: `On 'always'`,
                        action: () => console.log(`On 'always' clicked.`),
                    },
                    {
                        showOn: 'axis',
                        label: `On 'axis'`,
                        action: (ev) => console.log(`On 'axis' clicked -`, ev.axisId, ev),
                    },
                    {
                        showOn: 'caption',
                        label: `On 'caption'`,
                        action: ({ captionType, text }) =>
                            console.log(`On 'caption' clicked -`, captionType, text),
                    },
                    {
                        showOn: 'series-area',
                        label: `On 'series-area'`,
                        action: () => console.log(`On 'series-area' clicked.`),
                    },
                    {
                        showOn: 'series-node',
                        label: `On 'series-node'`,
                        action: ({ datum, xKey, yKey }) =>
                            console.log(`On 'series-node' clicked -`, yKey, datum[xKey], datum[yKey]),
                    },
                    {
                        showOn: 'legend-item',
                        label: `On 'legend-item'`,
                        action: ({ itemId }) => console.log(`On 'legend-item' clicked -`, itemId),
                    },
                ],
            },
        ],
    },
}
```

In this configuration:

- The built-in Zoom and Legend menu items are placed into Sub-Menus.
- An additional "Debug Console" Sub-Menu is added with custom actions to log events to the console.

## Dynamic Items

The `getItems` callback can be used to implement Dynamic Context Menus. This is typically used to write Menu Items that depend on underlying state or data in some way.

#### Dynamic Context Menu

```ts
import { createApp, defineComponent, ref } from "vue";
import { AgCharts } from "ag-charts-vue3";
import type { AgChartOptions } from "ag-charts-types";
import {
  BarSeriesModule,
  CategoryAxisModule,
  ContextMenuModule,
  LegendModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-enterprise";
import type {
  AgCartesianChartOptions,
  AgContextMenuItem,
} from "ag-charts-types";

type DatumType = {
  sector: string;
  nyse: number;
  lse: number;
  tyo: number;
};

function updateVisibility(seriesId, visible) {
  for (const series of options.series) {
    if (series.id === seriesId) {
      series.visible = visible;
    }
  }
  chart.update(options);
}

ModuleRegistry.registerModules([
  BarSeriesModule,
  NumberAxisModule,
  CategoryAxisModule,
  LegendModule,
  ContextMenuModule,
]);

const ChartExample = defineComponent({
  template: `
    <ag-charts
      :options="options"
    />
  `,
  components: {
    "ag-charts": AgCharts,
  },
  setup(props) {
    const options = ref<AgCartesianChartOptions<DatumType>>({
      title: {
        text: "Stock Investment Portfolio by Sector",
      },
      subtitle: {
        text: "Allocation (%) by Market",
      },
      data: [
        { sector: "Industrial", nyse: 28, lse: 22, tyo: 18 },
        { sector: "Financial", nyse: 24, lse: 30, tyo: 20 },
        { sector: "Energy", nyse: 20, lse: 18, tyo: 25 },
        { sector: "Technology", nyse: 15, lse: 10, tyo: 12 },
        { sector: "Healthcare", nyse: 8, lse: 13, tyo: 10 },
        { sector: "Consumer Staples", nyse: 5, lse: 7, tyo: 15 },
      ],
      series: [
        {
          id: "New York Stock Exchange",
          type: "bar",
          xKey: "sector",
          yKey: "nyse",
          yName: "NYSE",
        },
        {
          id: "London Stock Exchange",
          type: "bar",
          xKey: "sector",
          yKey: "lse",
          yName: "LSE",
        },
        {
          id: "Tokyo Stock Exchange",
          type: "bar",
          xKey: "sector",
          yKey: "tyo",
          yName: "TYO",
        },
      ],
      contextMenu: {
        getItems: (params) => {
          if (params.showOn === "series-node") {
            const xName = params.datum[params.xKey];
            return [
              "defaults",
              "separator",
              // Dynamic Context Menu Item
              {
                type: "action",
                showOn: "series-node",
                label: `Log Datum "${params.seriesId} - ${xName}"`,
                action: () => console.log(params.datum),
              },
            ];
          }
        },
      },
    });

    return {
      options,
    };
  },
});

createApp(ChartExample).mount("#app");
```

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

```js
{
    contextMenu: {
        getItems: (params) => {
            if (params.showOn === 'series-node') {
                const xName = params.datum[params.xKey];
                return [
                    'defaults',
                    'separator',
                    // Dynamic Context Menu Item
                    {
                        type: 'action',
                        showOn: 'series-node',
                        label: `Log Datum "${params.seriesId} - ${xName}"`,
                        action: () => console.log(params.datum),
                    },
                ];
            }
        },
    },
}
```

In this example:

- The Context Menu on Bar Nodes includes a console `Log Datum` Menu Item, whose text is dynamically derived from the Context Menu Event parameters.
- The `getItems` params includes a `showOn` property which indicates which element type was right-clicked, alongside relevant information about the clicked element.
- If there are multiple elements at the same click point, the `allShowOnParams` array on the `getItems` params provides the parameters for every element, not just the winning one.

### Example: Interactive Customisation

The `getItems` callback can be used to change the state or appearance of a data point or series.

#### Dynamic Context Menu Data

```ts
import { createApp, defineComponent, ref } from "vue";
import { AgCharts } from "ag-charts-vue3";
import type { AgChartOptions } from "ag-charts-types";
import {
  CategoryAxisModule,
  ContextMenuModule,
  LegendModule,
  LineSeriesModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-enterprise";
import type {
  AgCartesianChartOptions,
  AgContextMenuGetItemsParams,
  AgContextMenuItem,
  AgLineSeriesOptions,
} from "ag-charts-types";
import clone from "clone";

type DatumType = {
  year: string;
  usa: number | null;
  china: number | null;
  india: number | null;
};

const baseData = [
  { year: "2015", usa: 2.9, china: 7.0, india: 8.0 },
  { year: "2016", usa: 1.8, china: 6.9, india: 8.3 },
  { year: "2017", usa: 2.0, china: 5.9, india: 6.0 },
  { year: "2018", usa: 3.0, china: 5.7, india: 5.6 },
  { year: "2019", usa: 3.2, china: 6.0, india: 3.4 },
  { year: "2020", usa: -2.2, china: 2.2, india: -5.8 },
  { year: "2021", usa: 5.8, china: 8.4, india: 9.7 },
  { year: "2022", usa: 1.9, china: 3.0, india: 7.0 },
  { year: "2023", usa: 2.5, china: 5.2, india: 8.2 },
  { year: "2024", usa: 2.8, china: 5.0, india: 6.5 },
];

// Default theme palette fills, in palette order, so the initial colours match the default theme.
const seriesMeta = [
  {
    id: "United States",
    yKey: "usa",
    yName: "United States",
    color: "#5090dc",
  },
  { id: "China", yKey: "china", yName: "China", color: "#ffa03a" },
  { id: "India", yKey: "india", yName: "India", color: "#459d55" },
];

const seriesColors = [
  { label: "Blue", value: "#5090dc" },
  { label: "Green", value: "#459d55" },
  { label: "Orange", value: "#ffa03a" },
  { label: "Purple", value: "#9669cb" },
  { label: "Red", value: "#ef5452" },
];

const emphasisedPoints = new Set();

function pointKey(seriesId, year) {
  return `${seriesId}::${year}`;
}

function colorSwatch(color) {
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="12" cy="12" r="7" fill="${color}"/></svg>`;
  return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
}

function createSeries() {
  return seriesMeta.map(({ id, yKey, yName, color }) => ({
    id,
    type: "line",
    xKey: "year",
    yKey,
    yName,
    // Assign an explicit colour per series so the palette isn't reassigned by position when a series is removed.
    stroke: color,
    marker: {
      enabled: true,
      fill: color,
      stroke: color,
      itemStyler: ({ seriesId, datum }) =>
        emphasisedPoints.has(pointKey(seriesId, datum.year))
          ? { size: 14 }
          : {},
    },
    label: {
      enabled: true,
      color: { ref: "textColor", mix: 0.6, ontoColor: color },
      fill: { ref: "chartBackgroundColor", mix: 0.8, ontoColor: color },
      border: {
        enabled: true,
        stroke: color,
      },
      padding: 4,
      placement: ["top", "bottom", "left", "right"],
      collision: {
        alwaysShow: true,
      },
      itemStyler: ({ seriesId, datum }) => ({
        enabled: emphasisedPoints.has(pointKey(seriesId, datum.year)),
      }),
    },
  }));
}

let data = baseData.map((datum) => ({ ...datum }));

let series = createSeries();

ModuleRegistry.registerModules([
  LineSeriesModule,
  NumberAxisModule,
  CategoryAxisModule,
  LegendModule,
  ContextMenuModule,
]);

const ChartExample = defineComponent({
  template: `
    <div class="example-controls">
      <div class="controls-row">
        <button v-on:click="reset()"><code>Reset</code></button>
      </div>
    </div>
    <ag-charts
      :options="options"
    />
  `,
  components: {
    "ag-charts": AgCharts,
  },
  setup(props) {
    const options = ref<AgCartesianChartOptions<DatumType>>({
      title: {
        text: "Annual GDP Growth by Country",
      },
      subtitle: {
        text: "Right-click a point or legend item to change the point or its series",
      },
      theme: {
        overrides: {
          line: {
            series: {
              highlight: {
                highlightedItem: {
                  strokeWidth: 2,
                },
                unhighlightedSeries: {
                  opacity: 1,
                },
              },
            },
          },
        },
      },
      data,
      series,
      contextMenu: { getItems: (params) => getItems(params) },
    });

    const getItems = (params) => {
      if (params.showOn === "legend-item") {
        const { seriesId } = params;
        return [
          "toggle-series-visibility",
          {
            type: "action",
            showOn: "legend-item",
            label: `Remove "${seriesId}"`,
            action: () => removeSeries(seriesId),
          },
          {
            showOn: "legend-item",
            label: `Colour "${seriesId}"`,
            items: seriesColors.map((color) => ({
              type: "action",
              showOn: "legend-item",
              label: color.label,
              iconUrl: colorSwatch(color.value),
              action: () => colorSeries(seriesId, color.value),
            })),
          },
        ];
      }
      const result = [];
      for (const paramsEntry of params.allShowOnParams) {
        if (paramsEntry.showOn === "series-node") {
          const { yKey, xKey, seriesId } = paramsEntry;
          const year = String(paramsEntry.datum[xKey]);
          const yKeyFormatting = {
            china: "China",
            india: "India",
            usa: "USA",
            year: undefined,
          };
          const formattedDatum = `${yKeyFormatting[yKey]} ${year}`;
          const isEmphasised = emphasisedPoints.has(pointKey(seriesId, year));
          result.push(
            {
              type: "action",
              showOn: "series-node",
              label: `${isEmphasised ? "Remove Emphasis from" : "Emphasise"} "${formattedDatum}" Point`,
              action: () => toggleEmphasis(seriesId, year),
            },
            {
              type: "action",
              showOn: "series-node",
              label: `Remove "${formattedDatum}" Point`,
              action: () => removeDataPoint(year, yKey),
            },
          );
        }
      }
      return result;
    };
    const syncOptions = () => {
      const optionsCopy = clone(options.value);

      // Sync both data and series so every update reflects the full current state.
      optionsCopy.data = data;
      optionsCopy.series = series;

      options.value = optionsCopy;
    };
    const toggleEmphasis = (seriesId, year) => {
      const key = pointKey(seriesId, year);
      if (emphasisedPoints.has(key)) {
        emphasisedPoints.delete(key);
      } else {
        emphasisedPoints.add(key);
      }
      // Reassign the data so the styler and label re-run with the updated emphasis set.
      data = data.map((datum) => ({ ...datum }));
      syncOptions();
    };
    const removeDataPoint = (year, yKey) => {
      data = data.map((datum) =>
        datum.year === year ? { ...datum, [yKey]: null } : datum,
      );
      syncOptions();
    };
    const colorSeries = (seriesId, color) => {
      series = series.map((s) =>
        s.id === seriesId
          ? {
              ...s,
              stroke: color,
              marker: { ...s.marker, fill: color, stroke: color },
            }
          : s,
      );
      syncOptions();
    };
    const removeSeries = (seriesId) => {
      series = series.filter((s) => s.id !== seriesId);
      syncOptions();
    };
    const reset = () => {
      emphasisedPoints.clear();
      data = baseData.map((datum) => ({ ...datum }));
      series = createSeries();
      syncOptions();
    };

    return {
      options,
      reset,
    };
  },
});

createApp(ChartExample).mount("#app");
```

[Live example: Dynamic Context Menu Data](https://www.ag-grid.com/charts/vue3/context-menu/examples/dynamic-context-menu-data)

```js
{
    contextMenu: {
        getItems: (params) => {
            if (params.showOn === 'series-node') {
                const { seriesId } = params;
                const year = String(params.datum[params.xKey]);
                const yKey = params.yKey;
                const isEmphasised = emphasisedPoints.has(pointKey(seriesId, year));
                return [
                    {
                        type: 'action',
                        showOn: 'series-node',
                        label: `${isEmphasised ? 'Remove Emphasis from' : 'Emphasise'} "${year}" Point`,
                        action: () => toggleEmphasis(seriesId, year),
                    },
                    {
                        type: 'action',
                        showOn: 'series-node',
                        label: `Remove "${year}" Point`,
                        action: () => removeDataPoint(year, yKey),
                    },
                ];
            }
            if (params.showOn === 'legend-item') {
                const { seriesId } = params;
                return [
                    'toggle-series-visibility',
                    {
                        type: 'action',
                        showOn: 'legend-item',
                        label: `Remove "${seriesId}"`,
                        action: () => removeSeries(seriesId),
                    },
                    // A submenu of colour options
                    {
                        showOn: 'legend-item',
                        label: `Colour "${seriesId}"`,
                        items: seriesColors.map((color) => ({
                            type: 'action',
                            showOn: 'legend-item',
                            label: color.label,
                            action: () => colorSeries(seriesId, color.value),
                        })),
                    },
                ];
            }
        },
    },
}
```

In this example, right-clicking a data point shows custom actions for that point:

- "Emphasise" enlarges the marker and displays the value; triggering it again removes the emphasis.
- "Remove Point" sets the point's value to `null`, leaving a gap in the line.

Right-clicking a legend item shows actions for the whole series:

- The built-in `toggle-series-visibility` item hides or shows the series.
- "Remove Series" drops the series.
- "Colour" is a submenu of preset colours; selecting one recolours the series' line and markers.

## API Reference

#### Context Menu

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| enabled | boolean | true | Whether to show the context menu. |
| items | AgContextMenuItem[] | ['defaults'] | List of menu items (and submenus) for the context menu. |
| getItems | AgContextMenuGetItemsCallback | undefined | Callback to list the menu items (and submenus) for the context menu. Overrides `items` if return-value is defined, otherwise `items` is used as a fallback. |

#### Always

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn | 'always' | 'always' | Which clicked element this menu item should be shown for. `'always'` menu items are always shown. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Axis

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'axis' | 'axis' | Which clicked element this menu item should be shown for. `'axis'` menu items are when clicking any part of an axis. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Caption

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'caption' | 'caption' | Which clicked element this menu item should be shown for. `'caption'` menu items are when clicking on a caption (title, subtitle, footnote). |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Cross-Line

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'cross-line' |  | Which clicked element this menu item should be shown for. `'cross-line'` menu items are shown when right-clicking a cross line's line or fill. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Series-Area

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'series-area' |  | Which clicked element this menu item should be shown for. `'series-area'` menu items are shown when clicking anywhere within the series area bounds. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Series-Node

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'series-node' |  | Which clicked element this menu item should be shown for. `'series-node'` menu items are shown when clicking when clicking on a datum node. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Legend

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'legend-item' |  | Which clicked element this menu item should be shown for. `'legend-item'` menu items are shown when clicking on a legend item. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |
