在Monaco编辑器中禁用右键菜单中的剪切和复制功能

5

我正在使用monaco-editor,我发现在后续版本中上下文菜单中添加了剪切和复制功能。我想从上下文菜单中删除这两个选项。请告诉我如何实现?


1
你找到解决方案了吗? - Devid
5个回答

3

完整代码

import * as actions from "monaco-editor/esm/vs/platform/actions/common/actions";

let menus = actions.MenuRegistry._menuItems
let contextMenuEntry = [...menus].find(entry => entry[0]._debugName == "EditorContext")
let contextMenuLinks = contextMenuEntry[1]

let removableIds = ["editor.action.clipboardCopyAction", "editor.action.clipboardPasteAction"]

let removeById = (list, ids) => {
  let node = list._first
  do {
    let shouldRemove = ids.includes(node.element?.command?.id)
    if (shouldRemove) { list._remove(node) }
  } while ((node = node.next))
}

removeById(contextMenuLinks, removableIds)

操作步骤

您可以从actions.js中的MenuRegistry访问可用的菜单功能:

import * as actions from "monaco-editor/esm/vs/platform/actions/common/actions"
let menus = actions.MenuRegistry._menuItems

这将提供所有菜单类型的列表,例如:
["MenubarEditMenu", "CommandPalette", "EditorContext", ...] 要特别访问和修改上下文菜单,我们可以在菜单映射中找到它:
let contextMenuEntry = [...menus].find(entry => entry[0]._debugName == "EditorContext")
let contextMenuLinks = contextMenuEntry[1]

菜单项的类型是LinkedList,每个node包含一个element和对prevnext节点的引用,但它配备了一些实用程序方法,使得更容易进行推理。
所以如果你想列出所有命令,你可以这样做:
let allCommandIds = [...contextMenuLinks].map(el => el.command?.id)

使用这个方法,预先确定你想要提取的命令列表 - 在我们的情况下是:
let removableIds = [
  "editor.action.clipboardCopyAction",
  "editor.action.clipboardPasteAction",
]

接下来,我们需要识别并删除具有这些ID的节点。迭代器返回{{link1:node.element}},但{{link2:_remove()}}函数接受整个节点,因此我们需要以与之前略有不同的方式进行迭代。以下是一个循环遍历所有节点并删除每个节点的函数:
然后,我们将获取要删除的所有节点:
let removeById = (list, ids) => {
  let node = list._first;
  do {
    let shouldRemove = ids.includes(node.element?.command?.id)
    if (shouldRemove) { list._remove(node) }
  } while ((node = node.next))
}

And then call like this:

removeById(contextMenuLinks, removableIds)

演示

Screenshot Example

进一步阅读


2

使用CSS隐藏单个项目

我在浏览器中尝试了这段代码,它可以正常工作。

// Hide from cut on
.context-view li.action-item:nth-child(n + 9) {
    display: none !important;
}

// Show command palette
.context-view li.action-item:last-child {
  display: flex !important;
}

使用API禁用整个菜单
monacoOptions = {
  // other options
  contextmenu: false
}

请参阅有关 IEditorConstructionOptions > contextmenu 的文档。


嗨,我想显示上下文菜单,但禁用其中一些选项。我想从上下文菜单中隐藏复制、剪切和粘贴选项,但不想完全禁用上下文菜单。 - user2058239

1

编辑:此答案现已过时,有关最新解决方法,请查看:https://github.com/microsoft/monaco-editor/issues/1567

这里的答案已经过时了,actions.MenuRegistry._menuItems似乎不再返回任何内容。编辑器也已经移动到shadowroot,破坏了css方法。

以下方法可行:

const shadowroot = document.querySelector(".shadow-root-host").shadowRoot
const RemoveContextMenuIndexes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13];
for (const itemIndex of RemoveContextMenuIndexes) {
   shadowroot.querySelector(`ul.actions-container > li.action-item:nth-child(${itemIndex})`).style.display = "none";
}

如果您有多个编辑器,则会有更多具有shadow-root-host类的元素,必须通过循环来处理。

0

隐藏没有CSS的项目

以下内容适用于我,并且是从KyleMit的此答案进行修改的,因为原文对我不起作用。

import * as actions from 'monaco-editor/esm/vs/platform/actions/common/actions';

const idsToRemove = ['editor.action.clipboardCopyAction', 'editor.action.clipboardCutAction'];
actions.MenuRegistry._menuItems[1] = actions.MenuRegistry._menuItems[1]
        .filter(menuItem => !menuItem.command || !idsToRemove.includes(menuItem.command.id));

0

最終我採用了一種基於Nighfalls答案稍微修改的方法

  • 使用onContextMenu來訪問代碼編輯器。當用戶打開上下文菜單時,將運行此操作。在用戶第一次打開上下文菜單之前,DOM中不存在影子根。
  • setTimeout在這裡是必要的,以便於querySelector找到影子根和上下文菜單項,因為在用戶打開上下文菜單之前,影子根不存在於DOM中。
  • 試圖對操作項元素設置style.display會導致typescript linting問題。在元素本身上使用.remove()會導致菜單中其他更下面的動作項目的懸停/高亮顯示存在奇怪的錯誤。最終需要.remove()操作項元素內部的元素。
this.codeEditorInstance.onContextMenu(() => {
  setTimeout(() => {
    const shadowroot = document.querySelector('.shadow-root-host')?.shadowRoot;
    if (shadowroot) {
      const RemoveContextMenuIndexes = [3];
      for (const itemIndex of RemoveContextMenuIndexes) {
        const el = shadowroot.querySelector(
          `ul.actions-container > li.action-item:nth-child(${itemIndex})`
        );
        if (el) {
          el.querySelector('a')
            ?.querySelectorAll('span')
            .forEach((s) => s.remove());
          el.querySelector('a')?.remove();
        }
      }
    }
  }, 0);
});

值得一提的是,“粘贴”功能在Firefox中已经被移除了,这将影响您尝试删除的索引。因此,最好检查浏览器类型,并相应地删除索引。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接