Reputation: 159
I am using monaco-editor, i see that cut and copy is added in the context menu in later versions. i want to remove these two options from context menu. Please let me know how can i achieve it?
Upvotes: 6
Views: 3843
Reputation: 106
I faced the same challenge, and here's the solution that worked perfectly for me:
constructor(
private renderer: Renderer2,
) {}
ngOnInit(): void {
document.addEventListener('contextmenu', () => {
setTimeout(() => {
document.querySelectorAll('.action-item .action-label').forEach((item: HTMLElement) => {
if (item.getAttribute('aria-label') === 'Copy') {
this.renderer.setStyle(item.parentElement, 'display', 'none');
}
});
}, 100);
});
}
If this doesn't work for you, you can use the Inspect Elements tool to find the element's location in the context menu. To enable inspecting elements in the context menu, open the Developer Tool, press Ctrl + Shift + P
, and select "Emulated a focused page." This will allow you to inspect elements within the context menu. Once you find the location of the text, you can try adjusting the code accordingly.
Upvotes: 1
Reputation: 127
Edit: this awnser is now outdated too, for the latest workarounds, check: https://github.com/microsoft/monaco-editor/issues/1567
The awnsers here are outdated, actions.MenuRegistry._menuItems seems to no longer return anything. The editor has also moved to a shadowroot breaking the css method.
The following works:
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";
}
If you have multiple editors, you will have more elements with the shadow-root-host class, thru which you would have to loop.
Upvotes: 1
Reputation: 3312
I ended up with a slightly modified approach based on Nighfalls answer
onContextMenu
of the code editor. This will run when user opens the context menu. Before a user opens a context menu for the first time, shadow root does not exist in the DOM.querySelector
s to find shadow root and context menu items, after they have been added. Again, because shadow root does not exist in the DOM until user has opened the context menu once.style.display
on the action item element was causing typescript linting issues. .remove()
on the element itself was causing a weird bug with hovering/highlighting action items further down in the menu. Ended up needing to .remove()
the elements inside of the action item element.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);
});
also worth mentioning that 'paste' is already removed in Firefox, which will affect which indexes you're trying to remove. so it's probably a good idea to check browser type, and remove indexes accordingly.
Upvotes: 0
Reputation: 1
The following works for me and was adapted from this answer by KyleMit which did not work for me as stated.
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));
Upvotes: 0
Reputation: 30027
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)
You can access the available menu functions from MenuRegistry
inside actions.js
:
import * as actions from "monaco-editor/esm/vs/platform/actions/common/actions"
let menus = actions.MenuRegistry._menuItems
This will provide a list of all menu types: i.e.
["MenubarEditMenu", "CommandPalette", "EditorContext", ...]
To access and modify the context menu specifically, we can find it in the menu map:
let contextMenuEntry = [...menus].find(entry => entry[0]._debugName == "EditorContext")
let contextMenuLinks = contextMenuEntry[1]
The menu items are of type LinkedList
, where each node
contains an element
and a reference to the prev
and next
node, but it comes with some utility methods that make it easier to reason about.
So if you want to list all commands, you can do this:
let allCommandIds = [...contextMenuLinks].map(el => el.command?.id)
Using that, identify the list of commands you want to pluck out ahead of time - in our case:
let removableIds = [
"editor.action.clipboardCopyAction",
"editor.action.clipboardPasteAction",
]
Next we need to identify and remove the nodes with those ids. The iterator returns the node.element
, but the _remove()
function takes in the entire node, so we'll have to iterate a little different than before. Here's a function that loops through all nodes and removes each if
We'll then get all the nodes we want to 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)
Upvotes: 3
Reputation: 14002
I tried this code in the browser and it worked.
// 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;
}
monacoOptions = {
// other options
contextmenu: false
}
See Docs on IEditorConstructionOptions
> contextmenu
Upvotes: 2