# Customize annotation context menu
This guide will walk you through the following topics:
- Customizing context menu for supported annotations
- Customizing context menu for unsupported annotations
- Hiding a right click menu or menu items
- Showing a customized context menu
# Customize context menu for supported annotations
Web Viewer supports most of standard annotation types. Each type can have its own context menu. Below list the supported annotation and their corresponding XML element name.
{
"line": "fv--line-contextmenu",
"linearrow": "fv--linearrow-contextmenu",
"linedimension": "fv--linedimension-contextmenu",
"polylinedimension": "fv--polylinedimension-contextmenu",
"polygondimension": "fv--polygondimension-contextmenu",
"circle": "fv--circle-contextmenu",
"square": "fv--square-contextmenu",
"polyline": "fv--polyline-contextmenu",
"polygon": "fv--polygon-contextmenu",
"polygoncloud": "fv--polygoncloud-contextmenu",
"fileattachment": "fv--fileattachment-contextmenu",
"freetexttypewriter": "fv--freetexttypewriter-contextmenu",
"typewriter": "fv--typewriter-contextmenu",
"freetextcallout": "fv--freetextcallout-contextmenu",
"callout": "fv--callout-contextmenu",
"freetexttextbox": "fv--freetexttextbox-contextmenu",
"textbox": "fv--textbox-contextmenu",
"freetext": "fv--freetext-contextmenu",
"ink": "fv--ink-contextmenu",
"stamp": "fv--stamp-contextmenu",
"text": "fv--text-contextmenu",
"areahighlight": "fv--areahighlight-contextmenu",
"highlight": "fv--highlight-contextmenu",
"caret": "fv--caret-contextmenu",
"replace": "fv--replace-contextmenu",
"squiggly": "fv--squiggly-contextmenu",
"strikeout": "fv--strikeout-contextmenu",
"redact": "fv--redact-contextmenu",
"underline": "fv--underline-contextmenu",
"media": "fv--media-contextmenu",
"image": "fv--image-contextmenu",
"link": "fv--link-contextmenu",
"sound": "fv--sound-contextmenu"
}
The annotation element name can be accessed by using super.getAnnotsContextMenuName(owner)
in the UIExtension.XViewerUI. You may refer to the section Customizing viewerUI for a code example.
For the supported annotations, you can use the method UIExtension.UIConsts.FRAGMENT_ACTION.APPEND action to replace, add and remove menu items.
# Replacing menu items
new UIExtension.PDFUI({
appearance: UIExtension.appearances.AdaptiveAppearance.extend({
getDefaultFragments: function() {
return [{
target: 'fv--highlight-contextmenu',
action: UIExtension.UIConsts.FRAGMENT_ACTION.REPLACE,
template: `
<contextmenu name="fv--highlight-contextmenu">
<contextmenu-item-reply></contextmenu-item-reply>
<contextmenu-item-delete-annot></contextmenu-item-delete-annot>
<contextmenu-item-properties></contextmenu-item-properties>
<contextmenu-item name="x-user-custom-contextmenu-item">Custom </contextmenu-item>
</contextmenu>`,
config:[{
target: 'x-user-custom-contextmenu-item',
callback: function() {
alert('custom contextmenu item clicked!');
}
}]
}];
}
})
})
# Adding menu items
new UIExtension.PDFUI({
appearance: UIExtension.appearances.AdaptiveAppearance.extend({
getDefaultFragments: function() {
return [{
target: 'fv--textbox-contextmenu',
action: UIExtension.UIConsts.FRAGMENT_ACTION.APPEND,
template: `
<contextmenu-item name="x-user-custom-contextmenu-item">Custom</contextmenu-item>
`,
config: [{
target: 'x-user-custom-contextmenu-item',
callback: function() {
alert('custom contextmenu item clicked!');
}
}]
}];
}
})
});
# Removing menu items
new UIExtension.PDFUI({
appearance: UIExtension.appearances.AdaptiveAppearance.extend({
getDefaultFragments: function() {
return [{
target: 'fv--media-contextmenu>fv--contextmenu-item-media-download',
action: UIExtension.UIConsts.FRAGMENT_ACTION.REMOVE
}]
}
})
})
# Customize context menu for unsupported annotations
Unsupported annotations mean annotations that are not numerated in the above supported list but have already been defined in the PDF references. To customize the context menu for an unsupported annotation, you should rewrite getAnnotsContextMenuName in XViewUI to create a new context menu and then add it in template.
A quick way to check if the current annotation is supported or not in Web Viewer, you may check their corresponding element name, which is by default labeled as "fv--default-annot-contextmenu".
The following example is making assumption that your current PDF file contains a 'trapnet' annotation which is not yet supported in Web Viewer, and you want to customize its context menu.
new UIExtension.PDFUI({
viewerOptions: {
viewerUI: new class extends UIExtension.XViewerUI {
createContextMenu(owner, anchor, config) {
if(owner instanceof PDFViewCtrl.AnnotComponent) {
if(owner.annot.getType() === 'trapnet') {
return 'custom-trapnet-contextmenu-name';
}
}
return super.createContextMenu(owner, anchor, config);
}
} ()
},
appearance: UIExtension.appearances.AdaptiveAppearance.extend({
getDefaultFragments: function() {
return [{
target: 'template-container',
action: UIExtension.UIConsts.FRAGMENT_ACTION.APPEND,
template: `
<contextmenu name="custom-trapnet-contextmenu-name">
<contextmenu-item-reply></contextmenu-item-reply>
<contextmenu-item-delete-annot></contextmenu-item-delete-annot>
<contextmenu-item-properties></contextmenu-item-properties>
<contextmenu-item name="x-user-custom-contextmenu-item">Custom </contextmenu-item>
</contextmenu>
`,
config:[{
target: 'x-user-custom-contextmenu-item',
callback: function() {
alert('custom contextmenu item clicked!');
}
}]
}]
}
})
})
# Hiding the context menu or items
You can use one of the following approaches to achieve the hiding.
# 1. Configuring a class method in fragments to force the hiding action
new UIExtension.PDFUI({
appearance: UIExtension.appearances.AdaptiveAppearance.extend({
getDefaultFragments: function() {
// the other options ...
return [{
target: 'fv--underline-contextmenu',
config: {
cls: 'fv__ui-force-hide'
}
}]
}
})
})
The effect of this method is that there is no response following the right-clicking on the underline.
# 2. Customizing viewerUI
new UIExtension.PDFUI({
viewerOptions: {
viewerUI: new class extends UIExtension.XViewerUI {
createContextMenu(owner, anchor, config) {
if(owner instanceof PDFViewCtrl.AnnotComponent) {
const contextMenuName = super.getAnnotsContextMenuName(owner)
if(contextMenuName === 'fv--underline-contextmenu'){
return;
}
}
return super.createContextMenu(owner, anchor, config);
}
} ()
}
});
This method will hide the built-in menu when a right-clicking occurs, and present the browser default menu.
# 3. Overwrite the showContextMenu of AnnotComponent
const pdfui = new UIExtension.PDFUI({
// ....
});
pdfui.initializePromise.then(function () {
var annotMap = {};
pdfui.registerMatchRule(function(annot, AnnotComponentClass) {
let type = annot.getType();
var intent = annot.getIntent && annot.getIntent() || "default";
// You can add more annotation types
if(type === 'underline') {
return AnnotComponentClass;
}
if (annotMap[type] && annotMap[type][intent]) {
return annotMap[type][intent];
}
annotMap[type] = annotMap[type] || {};
return annotMap[type][intent] = (class extends AnnotComponentClass {
showContextMenu() {
// Do nothing
}
});
});
});
This method will hide the build-in menu and show the browser default menu of a right-clicking.
# Showing a customized context menu
You should overwrite the viewerUI to show your own context menu.
new UIExtension.PDFUI({
viewerOptions: {
viewerUI: new (class extends UIExtension.XViewerUI {
createContextMenu(owner, anchor, config) {
if (owner instanceof PDFViewCtrl.AnnotComponent) {
const contextMenuName = super.getAnnotsContextMenuName(owner);
if (contextMenuName === "fv--underline-contextmenu") {
return new (class extends PDFViewCtrl.IContextMenu {
constructor() {
super();
this.initContextmenu();
}
destroy() {
$(anchor).contextMenu("destroy");
}
showAt(x, y) {
$(anchor).contextMenu();
}
disable() {
super.disable();
$(anchor).contextMenu("destroy");
}
enable() {
super.enable();
this.initContextmenu();
}
initContextmenu() {
// The code example below requires referencing Jquery libraries including contextMenu.min.css, contextMenu.min.js and min.js.
$(anchor).contextMenu({
selector: config.selector,
items: [
{
name: 'show "Hello World"',
callback: function() {
alert("hello world");
}
},
{
name: 'show "Bye!"',
callback: function() {
alert("Bye!");
}
}
]
});
}
})();
}
}
return super.createContextMenu(owner, anchor, config);
}
})()
}
});