/** * This plugin allows inline cell editing of the pivot grid results. * * This plugin requires editors to be defined on the aggregate dimensions. * * { * xtype: 'pivotgrid', * plugins: [{ * ptype: 'pivotcellediting', * clicksToEdit: 1, * // define here the pivot updater to use: 'overwrite', 'increment', 'percentage', 'uniform' * defaultUpdater: 'overwrite' * }, * matrix: { * aggregate: [{ * dataIndex: 'value', * header: 'Total', * aggregator: 'sum', * // if you want an aggregate dimension to be editable you need to specify its editor * editor: 'numberfield' * }] * // ... * } * } * * The new value is applied to the records behind that cell using different approaches: * * - `percentage`: the user fills in a percentage that is applied to each record. * - `increment`: the user fills in a value that is added to each record. * - `overwrite`: the new value filled in by the user overwrites each record. * - `uniform`: replace sum of values with a provided value using uniform distribution * * More pivot updater types can be defined by extending {@link Ext.pivot.update.Base}. * * **Note:** Only works when using a {@link Ext.pivot.matrix.Local} matrix on a pivot grid. */Ext.define('Ext.pivot.plugin.CellEditing', { extend: 'Ext.grid.plugin.CellEditing', alias: 'plugin.pivotcellediting', requires: [ 'Ext.pivot.update.Increment', 'Ext.pivot.update.Overwrite', 'Ext.pivot.update.Percentage', 'Ext.pivot.update.Uniform' ], /** * @cfg {String} defaultUpdater * * Define which pivot updater is used when the cell value changes. */ defaultUpdater: 'uniform', /** * @cfg {Number/String} defaultValue * * Define a default value to show when cell editor is active. */ defaultValue: null, updater: null, init: function(grid){ var me = this; /** * Fires on the pivot grid before updating all result records. * * @event pivotbeforeupdate * @param {Ext.pivot.update.Base} updater Reference to the updater object */ /** * Fires on the pivot grid after updating all result records. * * @event pivotupdate * @param {Ext.pivot.update.Base} updater Reference to the updater object */ me.callParent([grid]); me.pivot = grid.isPivotGrid ? grid : grid.up('pivotgrid'); me.mon(me, { beforeedit: me.onBeforeEdit, // if users are listening to this event too then they should be allowed to change the context value // so we run first priority: 1000, scope: me }); if(!me.pivot.isCellEditingAttached) { me.mon(me.pivot, { pivotcolumnsbuilt: me.addColumnEditors, scope: me }); me.pivot.isCellEditingAttached = true; } me.updater = Ext.Factory.pivotupdate({type: me.defaultUpdater}); me.pivot.relayEvents(me.updater, ['beforeupdate', 'update'], 'pivot'); }, destroy: function(){ var me = this; me.pivot.isCellEditingAttached = false; me.pivot = me.updater = Ext.destroy(me.updater); me.callParent(); }, /** * Attach column editors when column configs are generated by the pivot grid. * * @param matrix * @param columns * * @private */ addColumnEditors: function(matrix, columns){ var len = columns.length, col, i; for(i = 0; i < len; i++){ col = columns[i]; if(col.dimension && col.dimension.editor){ // it has an aggregate dimension assigned col.editor = Ext.clone(col.dimension.editor); } if(col.columns){ this.addColumnEditors(matrix, col.columns); } } }, /** * Check if the cell is editable or not. * * A cell that doesn't have any pivot result it's not editable, which means that no record is available. * * @param {Ext.data.Model} record * @param {Ext.grid.column.Column} columnHeader * @return {Boolean} */ isCellEditable: function(record, columnHeader){ return this.callParent([record, columnHeader]) && Boolean(this.getPivotResult(this.getEditingContext(record, columnHeader))); }, onBeforeEdit: function(plugin, context){ if(!Ext.isEmpty(this.defaultValue)){ context.value = this.defaultValue; } return this.isCellEditable(context.record, context.column); }, getPivotResult: function(context){ var matrix = this.pivot.getMatrix(), leftItem = this.pivot.getLeftAxisItem(context.record), topItem = this.pivot.getTopAxisItem(context.column); return matrix.results.get(leftItem ? leftItem.key : matrix.grandTotalKey, topItem ? topItem.key : matrix.grandTotalKey); }, updateRecords: function(context, value){ var pivot = this.pivot, matrix = pivot.getMatrix(), leftItem = pivot.getLeftAxisItem(context.record), topItem = pivot.getTopAxisItem(context.column); this.updater.setConfig({ leftKey: leftItem ? leftItem.key : matrix.grandTotalKey, topKey: topItem ? topItem.key : matrix.grandTotalKey, matrix: matrix, dataIndex: context.column.dimension.dataIndex, value: value }); this.updater.update(); }, onEditComplete: function(ed, value, startValue){ var me = this, context = ed.context, view, record, result; view = context.view; record = context.record; context.value = value; if (!me.validateEdit(context)) { me.editing = false; return; } me.updateRecords(context, value); // Changing the record may impact the position context.rowIdx = view.indexOf(record); me.fireEvent('edit', me, context); // We clear down our context here in response to the CellEditor completing. // We only do this if we have not already started editing a new context. if (me.context === context) { me.setActiveEditor(null); me.setActiveColumn(null); me.setActiveRecord(null); me.editing = false; } }});