microsoft / monaco-editor

A browser based code editor
https://microsoft.github.io/monaco-editor/
MIT License
40.36k stars 3.59k forks source link

Allow Submenus in Context Menu #1947

Open oprobst88 opened 4 years ago

oprobst88 commented 4 years ago

Hi

I really like working with the Monaco Editor. I use it in a react app. I've started to use the context menu and using the addAction(descriptor: IActionDescriptor): IDisposable method to add customized entries. It would be really great to be able to add submenus. Otherwise, the context menu gets a bit messy...

Thanks Oliver

umesh-timalsina commented 4 years ago

This would really help. Is there a possibility to add a submenu?

y912765390 commented 3 years ago

Did you finally achieve it? I also wanted this feature, I tried to modify the source code to achieve the effect but it was difficult for me to introduce that many dependencies outside to achieve the effect of this feature. And if you did, can you tell me what you did in the end

oprobst88 commented 3 years ago

No. In the meantime I've switched to another editor.

smiles2424 commented 3 years ago

Is there a reason you closed this? I still think this is very much an issue I'd like to see resolved.

Its weird because VSCode has nested context menus.

sukh1495 commented 2 years ago

is there any update or any code snippet which can help to add sub actions for a action here like Peek in VS editor or monaco editor playground?

minzhenyu commented 2 years ago

@alexdima Is there a development plan for this feature?

dh666 commented 1 year ago

I like the default contextmenu of monaco-editor,I want to customize it.But I can't define the submenu by the method addAction. I'm not sure whether I can use the precondition, Do you have any solutions?

Ellen0604 commented 1 year ago

@hediet Is this issues achievable?

dh666 commented 1 year ago

这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。

NO-MAP commented 1 year ago

any update?

dh666 commented 1 year ago

这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。

IDovgalyuk commented 1 year ago

React sample that worked for me

import { editor } from 'monaco-editor';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { LinkedList } from 'monaco-editor/esm/vs/base/common/linkedList';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { MenuId, MenuRegistry } from 'monaco-editor/esm/vs/platform/actions/common/actions';

export const setupContextMenuFeature = (editor: editor.IStandaloneCodeEditor) => {
  removeAllMenus();

  addActionWithSubmenus(editor, {
    title: 'Change color',
    context: 'EditorChangeColorContext',
    group: 'edit',
    order: 0,
    actions: [
      {
        id: 'red',
        label: 'Red',
        run: () => console.log('red'),
      },
      {
        id: 'green',
        label: 'Green',
        run: () => console.log('green'),
      },
    ],
  });

  addActionWithSubmenus(editor, {
    title: 'Change coordinates',
    context: 'EditorChangeCoordinatesContext',
    group: 'edit',
    order: 1,
    actions: [
      {
        id: 'wgs',
        label: 'WGS',
        run: () => console.log('wgs'),
      },
      {
        id: 'utc',
        label: 'UTC',
        run: () => console.log('utc'),
      },
      {
        id: 'mgrs',
        label: 'MGRS',
        run: () => console.log('mgrs'),
      },
    ],
  });
};

const addActionWithSubmenus = (
  editor: editor.IStandaloneCodeEditor,
  descriptor: {
    title: string;
    context: string;
    group: string;
    order: number;
    actions: { run: (editor: editor.IStandaloneCodeEditor) => void; label: string; id: string }[];
  }
) => {
  const submenu = new MenuId(descriptor.context);
  const list = new LinkedList();
  MenuRegistry._menuItems.set(submenu, list);

  for (let i = 0; i < descriptor.actions.length; i++) {
    const action = descriptor.actions[i];
    editor.addAction({
      id: action.id,
      label: action.label,
      run: action.run,
      contextMenuOrder: i,
      contextMenuGroupId: descriptor.context,
    });
    const actionId = editor
      .getSupportedActions()
      .find(a => a.label === action.label && a.id.endsWith(action.id))!.id;

    const items = MenuRegistry._menuItems.get(MenuId.EditorContext) as LinkedList;
    const item = popItem(items, actionId);
    if (item) {
      list.push(item);
    }
  }

  MenuRegistry._menuItems.get(MenuId.EditorContext).push({
    group: descriptor.group,
    order: descriptor.order,
    submenu: submenu,
    title: descriptor.title,
  });
};

const popItem = (items: LinkedList, id: string): any => {
  let node = items._first;
  do {
    if (node.element?.command?.id === id) {
      items._remove(node);
      return node.element;
    }
    node = node.next;
  } while (node !== undefined);
};

const removeAllMenus = () => {
  const contextMenuEntry = MenuRegistry._menuItems.get(MenuId.EditorContext);
  let node = contextMenuEntry._first;
  do {
    if (node.element) {
      contextMenuEntry._remove(node);
    }
    node = node.next;
  } while (node !== undefined);
};

image

thunderstromcd commented 1 year ago

hi! any update of this issue?

CharlesHGong commented 1 year ago

Would be great if there are some public API for this

dh666 commented 1 year ago

这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。

Nyx2022 commented 12 months ago

React sample that worked for me对我有用的反应示例

import { editor } from 'monaco-editor';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { LinkedList } from 'monaco-editor/esm/vs/base/common/linkedList';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { MenuId, MenuRegistry } from 'monaco-editor/esm/vs/platform/actions/common/actions';

export const setupContextMenuFeature = (editor: editor.IStandaloneCodeEditor) => {
  removeAllMenus();

  addActionWithSubmenus(editor, {
    title: 'Change color',
    context: 'EditorChangeColorContext',
    group: 'edit',
    order: 0,
    actions: [
      {
        id: 'red',
        label: 'Red',
        run: () => console.log('red'),
      },
      {
        id: 'green',
        label: 'Green',
        run: () => console.log('green'),
      },
    ],
  });

  addActionWithSubmenus(editor, {
    title: 'Change coordinates',
    context: 'EditorChangeCoordinatesContext',
    group: 'edit',
    order: 1,
    actions: [
      {
        id: 'wgs',
        label: 'WGS',
        run: () => console.log('wgs'),
      },
      {
        id: 'utc',
        label: 'UTC',
        run: () => console.log('utc'),
      },
      {
        id: 'mgrs',
        label: 'MGRS',
        run: () => console.log('mgrs'),
      },
    ],
  });
};

const addActionWithSubmenus = (
  editor: editor.IStandaloneCodeEditor,
  descriptor: {
    title: string;
    context: string;
    group: string;
    order: number;
    actions: { run: (editor: editor.IStandaloneCodeEditor) => void; label: string; id: string }[];
  }
) => {
  const submenu = new MenuId(descriptor.context);
  const list = new LinkedList();
  MenuRegistry._menuItems.set(submenu, list);

  for (let i = 0; i < descriptor.actions.length; i++) {
    const action = descriptor.actions[i];
    editor.addAction({
      id: action.id,
      label: action.label,
      run: action.run,
      contextMenuOrder: i,
      contextMenuGroupId: descriptor.context,
    });
    const actionId = editor
      .getSupportedActions()
      .find(a => a.label === action.label && a.id.endsWith(action.id))!.id;

    const items = MenuRegistry._menuItems.get(MenuId.EditorContext) as LinkedList;
    const item = popItem(items, actionId);
    if (item) {
      list.push(item);
    }
  }

  MenuRegistry._menuItems.get(MenuId.EditorContext).push({
    group: descriptor.group,
    order: descriptor.order,
    submenu: submenu,
    title: descriptor.title,
  });
};

const popItem = (items: LinkedList, id: string): any => {
  let node = items._first;
  do {
    if (node.element?.command?.id === id) {
      items._remove(node);
      return node.element;
    }
    node = node.next;
  } while (node !== undefined);
};

const removeAllMenus = () => {
  const contextMenuEntry = MenuRegistry._menuItems.get(MenuId.EditorContext);
  let node = contextMenuEntry._first;
  do {
    if (node.element) {
      contextMenuEntry._remove(node);
    }
    node = node.next;
  } while (node !== undefined);
};

image

hello,can you tell us how can i call setupContextMenuFeature?or show us a full example to enable subcontextmenu

dh666 commented 12 months ago

这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。

IDovgalyuk commented 12 months ago

hello,can you tell us how can i call setupContextMenuFeature?or show us a full example to enable subcontextmenu

Sure, here is very basic sample: https://github.com/IDovgalyuk/monaco-editor-submenu-sample

Nyx2022 commented 12 months ago

hello,can you tell us how can i call setupContextMenuFeature?or show us a full example to enable subcontextmenu你好,你能告诉我们如何调用 setupContextMenuFeature 吗?或者向我们展示启用子上下文菜单的完整示例

Sure, here is very basic sample:当然,这是非常基本的示例: https://github.com/IDovgalyuk/monaco-editor-submenu-sample

thank you very much

Nyx2022 commented 11 months ago

hello,can you tell us how can i call setupContextMenuFeature?or show us a full example to enable subcontextmenu

Sure, here is very basic sample

i tried this sample, it's work well, is any body can convert setupContextMenuFeature.ts in src to common js that browser can run ?(without exports) thanh you !because i have no basic for nodejs or typescript development,so any body can help me ?