To add Clone and Delete functionality to the Row Editing Plugin. Using this override you will not have to implement “Delete” and “Clone” functionality, so in other words “All inclusive”. You can also add glyphs, move the “Cancel” button a little bit to the right to make the buttons notable.

I have overridden three classes:

The last two classes are private so it can stop working after the framework upgrade, but it works for versions 6.0.0 – 6.6.0. There is a bug in the row editor (v 6.6.0), by the first init it is moved to the right, on the second show it is centered.

Ext.define('overrides.grid.plugin.RowEditing', {
    override: 'Ext.grid.plugin.RowEditing',

    deleteButtonShow: false,
    cloneButtonShow: false,

    initEditorConfig: function(){
        var me       = this,
            grid     = me.grid,
            view     = me.view,
            headerCt = grid.headerCt,
            btns     = ['saveBtnText', 'deleteBtnTxt', 'cloneBtnTxt', 'cancelBtnText', 'errorsText', 'dirtyText'],
            b,
            bLen     = btns.length,
            cfg      = {
                autoCancel: me.autoCancel,
                autoUpdate: me.autoUpdate,
                removeUnmodified: me.removeUnmodified,
                errorSummary: me.errorSummary,
                formAriaLabel: me.formAriaLabel,
                formAriaLabelRowBase: me.formAriaLabelRowBase + (grid.hideHeaders ? -1 : 0),
                fields: headerCt.getGridColumns(),
                hidden: true,
                view: view,
                deleteButtonShow: me.deleteButtonShow,
                cloneButtonShow: me.cloneButtonShow,

                // keep a reference..
                editingPlugin: me
            },
            item;

        for (b = 0; b < bLen; b++) {
            item = btns[b];

            if (Ext.isDefined(me[item])) {
                cfg[item] = me[item];
            }
        }
        return cfg;
    },

    completeDelete: function() {
        var store = this.grid.getStore(),
            recordToDelete = this.getEditor().getRecord();
        store.remove(recordToDelete);
        return true;
    },

    completeClone: function() {
        var store = this.grid.getStore(),
            editor = this.getEditor(),
            form = editor.getForm(),
            recordToClone = this.getEditor().getRecord(),
            clonedRecord = recordToClone.copy(null);
        form.updateRecord(clonedRecord);
        store.add(clonedRecord);
        editor.hide();
        return true;
    }
});
Ext.define('overrides.grid.RowEditor', {
    override: 'Ext.grid.RowEditor',

    deleteBtnTxt: 'Delete',
    cloneBtnTxt: 'Clone',

    deleteButtonShow: false,
    cloneButtonShow: false
});
Ext.define('overrides.grid.RowEditorButtons', {
    override: 'Ext.grid.RowEditorButtons',

    deleteBtnTxt: 'Delete',

    constructor: function(config) {
        var me = this,
            rowEditor = config.rowEditor,
            cssPrefix = Ext.baseCSSPrefix,
            plugin = rowEditor.editingPlugin;

        config = Ext.apply({
            baseCls: cssPrefix + 'grid-row-editor-buttons',
            defaults: {
                xtype: 'button',
                ui: rowEditor.buttonUI,
                scope: plugin,
                flex: 1,
                minWidth: Ext.panel.Panel.prototype.minButtonWidth
            },
            items: [{
                cls: cssPrefix + 'row-editor-update-button',
                itemId: 'update',
                handler: plugin.completeEdit,
                text: rowEditor.saveBtnText,
                disabled: rowEditor.updateButtonDisabled,
                listeners: {
                    element: 'el',
                    keydown: me.onUpdateKeyDown,
                    scope: me
                }
            }, {
                cls: cssPrefix + 'row-editor-update-button',
                itemId: 'delete',
                handler: plugin.completeDelete,
                text: rowEditor.deleteBtnTxt,
                disabled: rowEditor.deleteButtonDisabled,
                hidden: !rowEditor.deleteButtonShow,
                listeners: {
                    element: 'el',
                    keydown: me.onDeleteKeyDown,
                    scope: me
                }
            }, {
                cls: cssPrefix + 'row-editor-update-button',
                itemId: 'clone',
                handler: plugin.completeClone,
                text: rowEditor.cloneBtnTxt,
                disabled: rowEditor.cloneButtonDisabled,
                hidden: !rowEditor.cloneButtonShow,
                listeners: {
                    element: 'el',
                    keydown: me.onCloneKeyDown,
                    scope: me
                }
            }, {
                cls: cssPrefix + 'row-editor-cancel-button',
                itemId: 'cancel',
                handler: plugin.cancelEdit,
                text: rowEditor.cancelBtnText,
                listeners: {
                    element: 'el',
                    keydown: me.onCancelKeyDown,
                    scope: me
                }
            }]
        }, config);

        me.callParent([config]);

        me.addClsWithUI(me.position);
    },

    onDeleteKeyDown: function(e) {
        console.log('Delete');
        if (e.shiftKey && e.getKey() === e.TAB) {
            e.stopEvent();
            // Must delay the focus, otherwise the imminent keyup will TAB off that field
            this.rowEditor.child(':focusable:not([isButton]):last').focus(false, true);
        }
    },

    onCloneKeyDown: function(e) {
        console.log('Clone');
        if (e.shiftKey && e.getKey() === e.TAB) {
            e.stopEvent();
            // Must delay the focus, otherwise the imminent keyup will TAB off that field
            this.rowEditor.child(':focusable:not([isButton]):last').focus(false, true);
        }
    }
});

Plugin initialization:

plugins: [{
    ptype: 'rowediting',
    clicksToEdit: 1,
    deleteButtonShow: true,
    cloneButtonShow: true
}],

Working sample. 🙂