3

Is there a way to add custom dynamic elements to the context menu in tinyMCE 4.x, after init? I created custom menu items but many of them have sub-items that are dependent on other things going on in my application.

I tried using editor.on('contextmenu') but the menu still does not update. Any ideas?

Eric Lease
  • 4,114
  • 1
  • 29
  • 45
kevin
  • 933
  • 1
  • 12
  • 20

3 Answers3

3
  1. Add the contextmenu plugin
  2. Override the default context menu (some plugins automatically add their own entries) by defining the contextmenu option. It is a pipe-delimited list of custom menu items (which you define in step 3)
  3. Define a list of custom menu items. These can have their own onclick event handlers, or define sub-menus.

tinymce.init({
    ...
    plugins: [..., 'contextmenu'],
    contextmenu: 'customItem1 | customItem2',
    setup: function (editor) {
        editor.addMenuItem('customItem1', {
            text: 'Menu Item 1',
            context: 'tools',
            onclick: function () {
                alert('Menu item 1 clicked');
            }
        });
        editor.addMenuItem('customItem2', {
            text: 'Menu Item 2',
            context: 'tools',
            menu: [ {
                text: "Sub-menu item 1",
                onclick: function () {
                    alert('Sub-menu item 1');
                }
            }, {
                text: "Sub-menu item 2",
                onclick: function () {
                    alert('Sub-menu item 2');
                }
            }]
        });
    }
});

References:

Eric Lease
  • 4,114
  • 1
  • 29
  • 45
  • But how do I make the submenu items a dynamic list? I am able to generate the submenu the the time tinymce is initiated but I need that list to be able to change dynamically. – kevin Jan 09 '18 at 20:28
  • 2
    @imnotfred I don't think there is a way to do what you want that is specific to TinyMCE. That is, you can't do it with the plugin or any events directly. You will have to bind to the `contextmenu` event, `preventDefault` when the event fires, and then show a custom context menu (some HTML that was previously hidden). There are lots of resources on how to create custom HTML/JS context menus. [Here's one](https://www.sitepoint.com/building-custom-right-click-context-menu-javascript/) – Eric Lease Jan 09 '18 at 21:04
  • Thanks for the info and the resources. It is a bit disappointing that tinyMCE doesn't provide this functionality, though. – kevin Jan 09 '18 at 21:26
  • editor.addMenuItem should now be editor.ui.registry.addMenuItem – bfredo123 Aug 22 '20 at 10:36
  • for newer versions use `editor.ui.registry.addMenuItem` instead of `editor.addMenuItem`. – Mubashar Abbas Jan 07 '22 at 11:49
0

Yes, it is possible. The JavaScript Object Function can be used to declare a value dynamically inside editor events. Even you can go for loops, but only one menu is supported in Dynamic (Since Context Menu Value is unique) make dummy context menu and declare separately (Apply your own logic).

On Sub-menu: to create a Dynamic Menu, use an Array and push it via JavaScript Object Methods in loops to display dynamically.

For Reference : Dynamic data added in custom TinyMCE Editor using AngularJs

  • 1
    could you try to explain a little bit better and edit your answer please. – Homungus Jul 15 '20 at 14:28
  • Call JavaScript Object Methods inside Menu In addMenuItem(dynamic_sub,{menu:object_methods}); for Sub-menu. For Dynamic Context Menu Use Same Logic(addMenuItem(dynamic_sub,object_methods_context);). – Venkatesh Manohar Jul 15 '20 at 15:08
0

This is how I did it I used jQuery $.each to iterate through my objects, you could also use vanilla JavaScript

//register plugin to process context menu on a specific tag
tinymce.PluginManager.add('contextmenu-plugin', function (editor) {
  var selectedCode
  // Create a function which returns an array of items, these can be Submenus or Simple Items
  var contextMenuItems = () => { 
    return [
    {
      type: 'submenu',
      text: "Submenu 1",
      getSubmenuItems: () => {
        if (selectedCode){
          var contextMenuItems = []
          $.each( ArrayWithData, (index, data ) => {
            contextMenuItems.push({
              type: 'item',
              text: `${data}`,
              onAction: () => {
                console.log("Clicked submenu option");
              }
            })
          })
          // return array of contextmenuitems -> this goes to the Submenu
          return contextMenuItems
        }
       }
      },
      {  
       icon: 'remove',
       text: 'Remove data',
       onAction: () => {
         console.log(`Removed data`)
       }
      }
      }
      ]
      }

// now register the contextmenu
editor.ui.registry.addContextMenu('contextmenu', {
  update: function (element) {
    //this way you can call contextMenuItems() every time you show the context menu
    return (element.tagName == "your-condition" && element.className.includes("another condition") ) ? contextMenuItems() : ""
        }
      });
    });