X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/grid/plugin/CellEditing.js diff --git a/src/grid/plugin/CellEditing.js b/src/grid/plugin/CellEditing.js new file mode 100644 index 00000000..647bc834 --- /dev/null +++ b/src/grid/plugin/CellEditing.js @@ -0,0 +1,366 @@ +/** + * @class Ext.grid.plugin.CellEditing + * @extends Ext.grid.plugin.Editing + * + * The Ext.grid.plugin.CellEditing plugin injects editing at a cell level for a Grid. Only a single + * cell will be editable at a time. The field that will be used for the editor is defined at the + * {@link Ext.grid.column.Column#field field}. The editor can be a field instance or a field configuration. + * + * If an editor is not specified for a particular column then that cell will not be editable and it will + * be skipped when activated via the mouse or the keyboard. + * + * The editor may be shared for each column in the grid, or a different one may be specified for each column. + * An appropriate field type should be chosen to match the data structure that it will be editing. For example, + * to edit a date, it would be useful to specify {@link Ext.form.field.Date} as the editor. + * + * {@img Ext.grid.plugin.CellEditing/Ext.grid.plugin.CellEditing.png Ext.grid.plugin.CellEditing plugin} + * + * ## Example Usage + * Ext.create('Ext.data.Store', { + * storeId:'simpsonsStore', + * fields:['name', 'email', 'phone'], + * data:{'items':[ + * {"name":"Lisa", "email":"lisa@simpsons.com", "phone":"555-111-1224"}, + * {"name":"Bart", "email":"bart@simpsons.com", "phone":"555--222-1234"}, + * {"name":"Homer", "email":"home@simpsons.com", "phone":"555-222-1244"}, + * {"name":"Marge", "email":"marge@simpsons.com", "phone":"555-222-1254"} + * ]}, + * proxy: { + * type: 'memory', + * reader: { + * type: 'json', + * root: 'items' + * } + * } + * }); + * + * Ext.create('Ext.grid.Panel', { + * title: 'Simpsons', + * store: Ext.data.StoreManager.lookup('simpsonsStore'), + * columns: [ + * {header: 'Name', dataIndex: 'name', field: 'textfield'}, + * {header: 'Email', dataIndex: 'email', flex:1, + * editor: { + * xtype:'textfield', + * allowBlank:false + * } + * }, + * {header: 'Phone', dataIndex: 'phone'} + * ], + * selType: 'cellmodel', + * plugins: [ + * Ext.create('Ext.grid.plugin.CellEditing', { + * clicksToEdit: 1 + * }) + * ], + * height: 200, + * width: 400, + * renderTo: Ext.getBody() + * }); + * + */ +Ext.define('Ext.grid.plugin.CellEditing', { + alias: 'plugin.cellediting', + extend: 'Ext.grid.plugin.Editing', + requires: ['Ext.grid.CellEditor'], + + constructor: function() { + /** + * @event beforeedit + * Fires before cell editing is triggered. The edit event object has the following properties
+ * + * @param {Ext.grid.plugin.Editing} editor + * @param {Object} e An edit event (see above for description) + */ + /** + * @event edit + * Fires after a cell is edited. The edit event object has the following properties
+ * + * + *

+grid.on('edit', onEdit, this);
+
+function onEdit(e) {
+    // execute an XHR to send/commit data to the server, in callback do (if successful):
+    e.record.commit();
+};
+         * 
+ * @param {Ext.grid.plugin.Editing} editor + * @param {Object} e An edit event (see above for description) + */ + /** + * @event validateedit + * Fires after a cell is edited, but before the value is set in the record. Return false + * to cancel the change. The edit event object has the following properties
+ * + * Usage example showing how to remove the red triangle (dirty record indicator) from some + * records (not all). By observing the grid's validateedit event, it can be cancelled if + * the edit occurs on a targeted row (for example) and then setting the field's new value + * in the Record directly: + *

+grid.on('validateedit', function(e) {
+  var myTargetRow = 6;
+
+  if (e.row == myTargetRow) {
+    e.cancel = true;
+    e.record.data[e.field] = e.value;
+  }
+});
+         * 
+ * @param {Ext.grid.plugin.Editing} editor + * @param {Object} e An edit event (see above for description) + */ + this.callParent(arguments); + this.editors = Ext.create('Ext.util.MixedCollection', false, function(editor) { + return editor.editorId; + }); + }, + + /** + * @private + * AbstractComponent calls destroy on all its plugins at destroy time. + */ + destroy: function() { + var me = this; + me.editors.each(Ext.destroy, Ext); + me.editors.clear(); + me.callParent(arguments); + }, + + // private + // Template method called from base class's initEvents + initCancelTriggers: function() { + var me = this; + grid = me.grid, + view = grid.view; + + me.mon(view, { + mousewheel: { + element: 'el', + fn: me.cancelEdit, + scope: me + } + }); + me.mon(grid, { + columnresize: me.cancelEdit, + columnmove: me.cancelEdit, + scope: me + }); + }, + + /** + * Start editing the specified record, using the specified Column definition to define which field is being edited. + * @param {Model} record The Store data record which backs the row to be edited. + * @param {Model} columnHeader The Column object defining the column to be edited. + * @override + */ + startEdit: function(record, columnHeader) { + var me = this, + ed = me.getEditor(record, columnHeader), + value = record.get(columnHeader.dataIndex), + context = me.getEditingContext(record, columnHeader); + + record = context.record; + columnHeader = context.column; + + // Complete the edit now, before getting the editor's target + // cell DOM element. Completing the edit causes a view refresh. + me.completeEdit(); + + // See if the field is editable for the requested record + if (columnHeader && !columnHeader.getEditor(record)) { + return false; + } + + if (ed) { + context.originalValue = context.value = value; + if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', context) === false || context.cancel) { + return false; + } + + me.context = context; + me.setActiveEditor(ed); + me.setActiveRecord(record); + me.setActiveColumn(columnHeader); + + // Defer, so we have some time between view scroll to sync up the editor + Ext.defer(ed.startEdit, 15, ed, [me.getCell(record, columnHeader), value]); + } else { + // BrowserBug: WebKit & IE refuse to focus the element, rather + // it will focus it and then immediately focus the body. This + // temporary hack works for Webkit and IE6. IE7 and 8 are still + // broken + me.grid.getView().el.focus((Ext.isWebKit || Ext.isIE) ? 10 : false); + } + }, + + completeEdit: function() { + var activeEd = this.getActiveEditor(); + if (activeEd) { + activeEd.completeEdit(); + } + }, + + // internal getters/setters + setActiveEditor: function(ed) { + this.activeEditor = ed; + }, + + getActiveEditor: function() { + return this.activeEditor; + }, + + setActiveColumn: function(column) { + this.activeColumn = column; + }, + + getActiveColumn: function() { + return this.activeColumn; + }, + + setActiveRecord: function(record) { + this.activeRecord = record; + }, + + getActiveRecord: function() { + return this.activeRecord; + }, + + getEditor: function(record, column) { + var editors = this.editors, + editorId = column.itemId || column.id, + editor = editors.getByKey(editorId); + + if (editor) { + return editor; + } else { + editor = column.getEditor(record); + if (!editor) { + return false; + } + + // Allow them to specify a CellEditor in the Column + if (!(editor instanceof Ext.grid.CellEditor)) { + editor = Ext.create('Ext.grid.CellEditor', { + editorId: editorId, + field: editor + }); + } + editor.parentEl = this.grid.getEditorParent(); + // editor.parentEl should be set here. + editor.on({ + scope: this, + specialkey: this.onSpecialKey, + complete: this.onEditComplete, + canceledit: this.cancelEdit + }); + editors.add(editor); + return editor; + } + }, + + /** + * Get the cell (td) for a particular record and column. + * @param {Ext.data.Model} record + * @param {Ext.grid.column.Colunm} column + * @private + */ + getCell: function(record, column) { + return this.grid.getView().getCell(record, column); + }, + + onSpecialKey: function(ed, field, e) { + var grid = this.grid, + sm; + if (e.getKey() === e.TAB) { + e.stopEvent(); + sm = grid.getSelectionModel(); + if (sm.onEditorTab) { + sm.onEditorTab(this, e); + } + } + }, + + onEditComplete : function(ed, value, startValue) { + var me = this, + grid = me.grid, + sm = grid.getSelectionModel(), + dataIndex = me.getActiveColumn().dataIndex; + + me.setActiveEditor(null); + me.setActiveColumn(null); + me.setActiveRecord(null); + delete sm.wasEditing; + + if (!me.validateEdit()) { + return; + } + me.context.record.set(dataIndex, value); + me.fireEvent('edit', me, me.context); + }, + + /** + * Cancel any active editing. + */ + cancelEdit: function() { + var me = this, + activeEd = me.getActiveEditor(), + viewEl = me.grid.getView().el; + + me.setActiveEditor(null); + me.setActiveColumn(null); + me.setActiveRecord(null); + if (activeEd) { + activeEd.cancelEdit(); + viewEl.focus(); + } + }, + + /** + * Starts editing by position (row/column) + * @param {Object} position A position with keys of row and column. + */ + startEditByPosition: function(position) { + var me = this, + grid = me.grid, + sm = grid.getSelectionModel(), + editRecord = grid.store.getAt(position.row), + editColumnHeader = grid.headerCt.getHeaderAtIndex(position.column); + + if (sm.selectByPosition) { + sm.selectByPosition(position); + } + me.startEdit(editRecord, editColumnHeader); + } +}); \ No newline at end of file