# 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 replace, add and remove menu items through the fragment configuration using UIExtension.UIConsts.FRAGMENT_ACTION.REPLACE, UIExtension.UIConsts.FRAGMENT_ACTION.APPEND and UIExtension.UIConsts.FRAGMENT_ACTION.REMOVE action.

# 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);
      }
    })()
  }
});