/** * Internal utility class that provides default configuration for cell editing. * @private */Ext.define('Ext.grid.CellEditor', { extend: 'Ext.Editor', alignment: 'l-l?', hideEl : false, cls: Ext.baseCSSPrefix + 'small-editor ' + Ext.baseCSSPrefix + 'grid-editor ' + Ext.baseCSSPrefix + 'grid-cell-editor', treeNodeSelector: '.' + Ext.baseCSSPrefix + 'tree-node-text', shim: false, shadow: false, useBoundValue: false, constructor: function(config) { var field; // Editor must appear at the top so that it does not contribute to scrollbars. this.y = 0; config = Ext.apply({}, config); field = config.field; if (field) { field.monitorTab = false; } this.callParent([config]); }, // Set the grid that owns this editor. // Usually this will only *change* once, and the renderTo will cause // rendering into the owning grid. // However in a Lockable assembly the editor has to swap sides if the column is moved across. // Called by CellEditing#getEditor setGrid: function(grid) { var me = this, oldGrid = me.grid, view, viewListeners; if (grid !== oldGrid) { viewListeners = { beforerefresh: me.beforeViewRefresh, refresh: me.onViewRefresh, scope: me }; // Remove previous refresh listener if (oldGrid) { oldGrid.getView().un(viewListeners); } // Set the renderTo target to reflect new grid view ownership view = grid.getView(); me.renderTo = view.getTargetEl().dom; me.grid = grid; // On view refresh, we need to copy our DOM into the detached body to prevent it from being garbage collected. view.on(viewListeners); } }, afterFirstLayout: function(width, height) { // After we've been laid out, we can get rid of the y property, we don't want // to be positioned now delete this.y; this.callParent([width, height]); }, beforeViewRefresh: function () { var me = this, dom = me.el && me.el.dom; if (dom) { if (me.editing) { // Clear the Panel's cellFocused flag prior to removing it from the DOM // This will prevent the Panels onFocusLeave from processing the resulting blurring. me.grid.view.cellFocused = false; // Set the Editor.allowBlur setting so that it does not process the upcoming field blur event and terminate the edit me.wasAllowBlur = me.allowBlur; me.allowBlur = false; } // Remove the editor from the view to protect it from anihilation: https://sencha.jira.com/browse/EXTJSIV-11713 if (dom.parentNode) { dom.parentNode.removeChild(dom); } } }, onViewRefresh: function () { var me = this, dom = me.el && me.el.dom; if (dom) { // If the view was refreshed while we were editing, replace it. if (me.editing) { me.allowBlur = me.wasAllowBlur; me.renderTo.appendChild(dom); // The removal will have blurred, so avoid the processing in onFocusEnter by restoring the previous // cellFocused setting me.grid.view.cellFocused = true; me.field.focus(); } else { Ext.getDetachedBody().dom.appendChild(dom); } } }, startEdit: function (boundEl, value) { this.context = this.editingPlugin.context; this.callParent([boundEl, value]); }, /** * @private * Shows the editor, end ensures that it is rendered into the correct view * Hides the grid cell inner element when a cell editor is shown. */ onShow: function() { var me = this, innerCell = me.boundEl.first(); // If we have had our owning grid changed (by a column switching sides in a Lockable assembly) // or, if a view refresh has removed us from the DOM // append this component into its renderTo target. if (me.el.dom.parentNode !== me.renderTo) { me.renderTo.appendChild(me.el.dom); } if (innerCell) { if (me.isForTree) { innerCell = innerCell.child(me.treeNodeSelector); } innerCell.hide(); } me.callParent(arguments); }, completeEdit: function(remainVisible) { var me = this, context = me.context; if (me.editing) { context.value = me.field.value; if (me.editingPlugin.validateEdit(context) === false) { if (context.cancel) { context.value = me.originalValue; me.editingPlugin.cancelEdit(); } return !!context.cancel; } } me.callParent([remainVisible]); }, onEditComplete: function(remainVisible) { // When being asked to process edit completion, if we are not hiding, restore the cell now if (remainVisible) { this.restoreCell(); } this.callParent(arguments); }, /** * @private * Shows the grid cell inner element when a cell editor is hidden */ onHide: function() { this.restoreCell(); // In IE and Edge, hiding focused element will make it lose focus. Later on // in CellEditing plugin we want to know if the active editor is focused; // but by that time the focus has been lost. this.currentlyFocused = !!this.el.contains(Ext.Element.getActiveElement()); this.callParent(arguments); }, restoreCell: function() { var me = this, innerCell = me.boundEl.first(); if (innerCell) { if (me.isForTree) { innerCell = innerCell.child(me.treeNodeSelector); } innerCell.show(); } }, /** * @private * Fix checkbox blur when it is clicked. */ afterRender: function() { var me = this, field = me.field; me.callParent(arguments); if (field.isCheckbox) { field.mon(field.inputEl, { mousedown: me.onCheckBoxMouseDown, click: me.onCheckBoxClick, scope: me }); } }, /** * @private * Because when checkbox is clicked it loses focus completeEdit is bypassed. */ onCheckBoxMouseDown: function() { this.completeEdit = Ext.emptyFn; }, /** * @private * Restore checkbox focus and completeEdit method. */ onCheckBoxClick: function() { delete this.completeEdit; this.field.focus(false, 10); }, /** * @private * Realigns the Editor to the grid cell, or to the text node in the grid inner cell * if the inner cell contains multiple child nodes. */ realign: function(autoSize) { var me = this, boundEl = me.boundEl, innerCell = boundEl.first(), innerCellTextNode = innerCell.dom.firstChild, width = boundEl.getWidth(), offsets = Ext.Array.clone(me.offsets), grid = me.grid, xOffset, v = '', // innerCell is empty if there are no children, or there is one text node, and it contains whitespace isEmpty = !innerCellTextNode || (innerCellTextNode.nodeType === 3 && !(Ext.String.trim(v = innerCellTextNode.data).length)); if (me.isForTree) { // When editing a tree, adjust the width and offsets of the editor to line // up with the tree cell's text element xOffset = me.getTreeNodeOffset(innerCell); width -= Math.abs(xOffset); offsets[0] += xOffset; } if (grid.columnLines) { // Subtract the column border width so that the editor displays inside the // borders. The column border could be either on the left or the right depending // on whether the grid is RTL - using the sum of both borders works in both modes. width -= boundEl.getBorderWidth('rl'); } if (autoSize === true) { me.field.setWidth(width); } // https://sencha.jira.com/browse/EXTJSIV-10871 Ensure the data bearing element has a height from text. if (isEmpty) { innerCell.dom.innerHTML = 'X'; } me.alignTo(innerCell, me.alignment, offsets); if (isEmpty) { innerCell.dom.firstChild.data = v; } }, // private getTreeNodeOffset: function(innerCell) { return innerCell.child(this.treeNodeSelector).getOffsetsTo(innerCell)[0]; }, onEditorTab: function(e){ var field = this.field; if (field.onEditorTab) { field.onEditorTab(e); } }, onFocusLeave : function(e) { this.callParent([e]); // Reset the flag that may have been set by CellEditing#startEdit to prevent // Ext.Editor#onFieldBlur from canceling editing. this.selectSameEditor = false; }, privates: { // In cell editing, focus is always under programmatic control. // All TAB key events are handled. // Both cancel and complete edit explicitly focuses the context cell. revertFocus : function() { // Ensure that hide processing does not throw focus back to the previously focused element. this.previousFocus = null; this.callParent(); } }});