How to create inline menu buttons

In order to make some tasks for the user as intuitive as possible, it might be interesting to add buttons with certain functionality directly to the element they would perform an action on. Those inline buttons, placed directly within the document view, might look like this: Inline Menu

First of all, a display only (see frame only nodes for more details) needs to be created, which displays the menu when clicked. To do this, add the following in the file types_overrides.js:

// in types_overrides.js dummy element erstellen
types.YOURELEMENT.finalizeForFrame = function(editor, element) {
// replace YOURELEMENT with your element
var div = editor.contentFrame.iframeDoc.createElement('div');
div.setAttribute('data-frameonly', 'true');
div.className = 'xe-custom-menu-1';
element.insertBefore(div, element.dom.firstChild);
};

Now a click-listener must be registered so that we can intercept the corresponding event. In order to do this, add the following snippet in the init.js file:

editor.contentFrame.iframeDoc.onmousedown = Ext.bind(editor.configObj.configData.onMouseDownReplacement, editor);

Now the corresponding listener must be implemented. To do this, add the following lines to the listeners.js file:

onMouseDownReplacement: function(e) {
// check editor mode
if (this.editorMode !== 'edit') {
// no lookup
return;
}
if (['xe-custom-menu-1'].indexOf(e.target.className) !== -1) {
// prevent default
e.preventDefault();
// create and open menu
this.configObj.configData.openCustomMenu(this, e);
return;
}
}

Now the actual logic that creates and displays the menu can be implemented. Insert the following sample code:

// build and show menu
openCustomMenu: function(editor, e) {
// get matching node
var node = editor.document.getNode(e.target.parentNode);
// check if document node is an Element
if (!(node instanceof Ext.ux.xeditor.Element)) {
return;
}
// get document node
var documentNode = node.getDocumentNode();
// create menu
var menu= editor.configObj.configData.buildContainerElementMenu(editor, documentNode);
// get mouse position
var iframeY = editor.contentFrame.iframeEl.dom.getBoundingClientRect().top;
// show menu
menu.showAt(e.clientX, e.clientY + iframeY);
},
buildContainerElementMenu: function(editor, element) {
var elementType = element.getType();
// sample item for removing element
var removeItem = {
iconCls: 'fa-trash',
text: global_phrases['global.remove'],
editorElement: element,
listeners: {
scope: editor,
click: function(item, e, eOpts) {
// select element before remove in order to have it selected if action is undone
editor.selectionManager.selectElement(item.editorElement, true);
// remove element
if (item.editorElement.removeDeep().selectionCorrected) {
// update editor state and sync selection
editor.updateState();
editor.selectionManager.sync();
// scroll to selection
var scrollResult = editor.contentFrame.scrollToSelection('middle', true);
if (scrollResult.selectionCorrected) {
// update editor state and sync selection
editor.updateState();
editor.selectionManager.sync();
}
}
}
}
};
// store menu items
var menuItems = [removeItem];
// create menu
var menu = Ext.create('Ext.menu.Menu', {
items: menuItems,
listeners: {
show: function(menu, eOpts) {
// add to active menus
editor.activeMenus.push(menu);
},
hide: function(menu, eOpts) {
// remove from active menus
Ext.Array.remove(editor.activeMenus, menu);
}
}
});
return menu;
},

Please note that the div element created in the first step with the class xe-custom-menu-1 still needs to be adapted using CSS so that it becomes visible (content, desired styling). This is done in the file editor.css.