Bulk edit ExtJs has row and cell editing plugins. Unfortunately they do not allow the cells in bulk/batch mode. I have tried to implement bulk edit using custom selection replicator. and popup form for single and multiple columns edit.

Custom selection replicator will replicate all the cells regardless if cell is editable or not. So I had to extend and create new plugin to implement this feature.

Ext.define('app.view.selection.Replicator', {
    extend: 'Ext.grid.selection.Replicator',
    alias: 'plugin.customselectionreplicator',
    replicateSelection: function (ownerGrid, sel, extension) {
        // This can only handle extending rows
        if (extension.columns || sel.isColumns) {
            return;
        }

        // eslint-disable-next-line vars-on-top
        var me = this,
            columns = me.columns,
            selFirstRowIdx = sel.getFirstRowIndex(),
            selLastRowIdx = sel.getLastRowIndex(),
            selectedRowCount = selLastRowIdx - selFirstRowIdx + 1,
            lastTwoRecords = [],
            colCount, i, j, column, values, startIdx, endIdx, increment, store, record,
            prevValues, prevValue, x, y;

        colCount = columns.length;
        store = columns[0].getView().dataSource;

        // Single row, just duplicate values into extension
        if (selectedRowCount === 1) {
            values = me.getColumnValues(sel.view.dataSource.getAt(selFirstRowIdx));
        }
        // Multiple rows, take the numeric values from the closest two rows,
        // calculate an array of differences and propagate it
        else {
            values = new Array(colCount);

            if (extension.rows < 0) {
                lastTwoRecords = [
                    store.getAt(selFirstRowIdx + 1),
                    store.getAt(selFirstRowIdx)
                ];
            } else {
                lastTwoRecords = [
                    store.getAt(selLastRowIdx - 1),
                    store.getAt(selLastRowIdx)
                ];
            }

            lastTwoRecords[0] = me.getColumnValues(lastTwoRecords[0]);
            lastTwoRecords[1] = me.getColumnValues(lastTwoRecords[1]);

            // The values array will be the differences between all numeric columns
            // in the selection of the closet two records.
            for (j = 0; j < colCount; j++) {
                x = lastTwoRecords[1][j];
                y = lastTwoRecords[0][j];

                if (!isNaN(x) && !isNaN(y)) {
                    values[j] = Number(x) - Number(y);
                }
            }
        }

        // Loop from end to start of extension area
        if (extension.rows < 0) {
            startIdx = extension.end.rowIdx;
            endIdx = extension.start.rowIdx - 1;
            increment = -1;
        } else {
            startIdx = extension.start.rowIdx;
            endIdx = extension.end.rowIdx + 1;
            increment = 1;
        }

        // Replicate single selected row
        if (selectedRowCount === 1) {
            for (i = startIdx; i !== endIdx; i += increment) {
                record = store.getAt(i);

                for (j = 0; j < colCount; j++) {
                    column = columns[j];

                    if (column.dataIndex && column.editor) {
                        record.set(column.dataIndex, values[j]);
                    }
                }
            }
        }
        // Add differences from closest two rows
        else {
            for (i = startIdx; i !== endIdx; i += increment) {
                record = store.getAt(i);
                prevValues = me.getColumnValues(store.getAt(i - increment));

                for (j = 0; j < colCount; j++) {
                    column = columns[j];

                    if (column.dataIndex) {
                        prevValue = prevValues[j];

                        if (!isNaN(prevValue)) {
                            record.set(
                                column.dataIndex,
                                Ext.coerce(Number(prevValue) + values[j], prevValue)
                            );
                        }
                    }
                }
            }
        }
    },
});

As you can see I just check if column has editor and allow the record edit.

The second method is to use popup forms to edit single or all the edited columns.

Fiddle