/** * * This plugin allows the user to modify records behind a pivot cell. * The user has to double click that cell to open the range editor window. * * The following types of range editing are available: * - 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. * - uniformly: the user fills in a value that is uniformly applied to each record. * */Ext.define('Ext.pivot.plugin.RangeEditor', { alternateClassName: [ 'Mz.pivot.plugin.RangeEditor' ], alias: [ 'plugin.pivotrangeeditor', 'plugin.mzrangeeditor' ], extend: 'Ext.AbstractPlugin', requires: [ 'Ext.pivot.Grid', 'Ext.window.Window', 'Ext.form.field.Text', 'Ext.form.field.Number', 'Ext.form.field.ComboBox', 'Ext.form.field.Display', 'Ext.button.Button', 'Ext.data.Store' ], mixins: { observable: 'Ext.util.Observable' }, /** * @cfg {Number} width Width of the viewer's window. */ width: null, /** * @cfg {Number} height Height of the viewer's window. */ height: null, /** * @cfg {String} textWindowTitle Range editor window title */ textWindowTitle: 'Range editor', /** * @cfg {String} textFieldValue Range editor field Value label */ textFieldValue: 'Value', /** * @cfg {String} textFieldEdit Range editor field Edit label */ textFieldEdit: 'Field', /** * @cfg {String} textFieldType Range editor field Type label */ textFieldType: 'Type', /** * @cfg {String} textButtonOk Range editor window Ok button text */ textButtonOk: 'Ok', /** * @cfg {String} textButtonCancel Range editor window Cancel button text */ textButtonCancel: 'Cancel', /** * @cfg {String} textTypePercentage Type of range editing */ textTypePercentage: 'Percentage', /** * @cfg {String} textTypeIncrement Type of range editing */ textTypeIncrement: 'Increment', /** * @cfg {String} textTypeOverwrite Type of range editing */ textTypeOverwrite: 'Overwrite', /** * @cfg {String} textTypeUniformly Type of range editing */ textTypeUniformly: 'Uniformly', /** * @cfg {Function} onBeforeRecordsUpdate Provide a function to handle the records update. * This one will be fired before updating the records. Return false if you want to stop the process. * The function receives the following arguments: pivot, colDefinition, records, newValue, oldValue */ onBeforeRecordsUpdate: Ext.emptyFn, /** * @cfg {Function} onAfterRecordsUpdate Provide a function to handle the records update. * This one will be fired after all records were updated. "sync" could be called on the store inside this function. * The function receives the following arguments: pivot, colDefinition, records, newValue, oldValue */ onAfterRecordsUpdate: Ext.emptyFn, /** * `"both"` (the default) - The plugin is added to both grids * `"top"` - The plugin is added to the containing Panel * `"locked"` - The plugin is added to the locked (left) grid * `"normal"` - The plugin is added to the normal (right) grid * * @private */ lockableScope: 'top', init: function(grid){ var me = this; //<debug> // this plugin is available only for the pivot grid if (!grid.isPivotGrid) { Ext.raise('This plugin is only compatible with Ext.pivot.Grid'); } //</debug> me.pivot = grid; me.pivotListeners = me.pivot.on({ pivotitemcelldblclick: me.runPlugin, pivotgroupcelldblclick: me.runPlugin, pivottotalcelldblclick: me.runPlugin, scope: me, destroyable: true }); me.callParent(arguments); }, destroy: function(){ var me = this; Ext.destroyMembers(me, 'view', 'pivotListeners'); me.pivot = me.view = me.pivotListeners = me.currentRecord = me.currentCol = me.currentResult = null; me.callParent(arguments); }, runPlugin: function(params, e, eOpts){ var me = this, matrix = me.pivot.getMatrix(), dataIndex; // do nothing if the plugin is disabled if(me.disabled) { return; } if(params.topKey){ me.initEditorWindow(); me.currentResult = matrix.results.get(params.leftKey, params.topKey); if(me.currentResult){ me.currentCol = params.column; dataIndex = me.currentCol.dimension.getId(); me.view.down('form').getForm().setValues({ field: me.currentCol.dimension.header || me.currentCol['text'] || me.currentCol.dimension.dataIndex, value: me.currentResult.getValue(dataIndex), type: 'uniformly' }); me.view.show(); } } }, updateRecords: function(){ var me = this, result = me.currentResult, colDef = me.currentCol, agg = colDef.dimension.getId(), dataIndex = colDef.dimension.dataIndex, values = me.view.down('form').getForm().getValues(), records, remainder = 0; records = result.records; if(me.onBeforeRecordsUpdate(me.pivot, colDef, records, values.value, result.getValue(agg)) === false){ return; } me.view.getEl().mask(); values.value = parseFloat(values.value); Ext.defer(function(){ Ext.Array.each(records, function(item){ var currValue = item.get(dataIndex), newValue, v; switch(values.type){ case 'percentage': v = Math.floor(currValue * values.value / 100); break; case 'increment': v = currValue + values.value; break; case 'overwrite': v = values.value; break; case 'uniformly': newValue = (1 / records.length * values.value) + remainder; v = Math.floor(newValue); remainder += (newValue - v); break; } // only apply a change if there is actually a change if( currValue != v ) { item.set(dataIndex, v); } }); me.onAfterRecordsUpdate(me.pivot, colDef, records, values.value, result.getValue(agg)); me.view.getEl().unmask(); me.view.close(); }, 10); }, initEditorWindow: function(){ var me = this; if(!me.view){ // create the editor window me.view = Ext.create('Ext.window.Window', { title: me.textWindowTitle, width: me.width, height: me.height, layout: 'fit', modal: true, closeAction: 'hide', items: [{ xtype: 'form', padding: 5, border: false, defaults: { anchor: '100%' }, items: [{ fieldLabel: me.textFieldEdit, xtype: 'displayfield', name: 'field' },{ fieldLabel: me.textFieldType, xtype: 'combo', name: 'type', queryMode: 'local', valueField: 'id', displayField: 'text', editable: false, store: Ext.create('Ext.data.Store',{ fields: ['id', 'text'], data: [ {'id': 'percentage', 'text': me.textTypePercentage}, {'id': 'increment', 'text': me.textTypeIncrement}, {'id': 'overwrite', 'text': me.textTypeOverwrite}, {'id': 'uniformly', 'text': me.textTypeUniformly} ] }) },{ fieldLabel: me.textFieldValue, xtype: 'numberfield', name: 'value' }] }], buttons: [{ text: me.textButtonOk, handler: me.updateRecords, scope: me },{ text: me.textButtonCancel, handler: function(){ this.view.close(); }, scope: me }] }); } } });