3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * This class specifies the definition for a column inside a {@link Ext.grid.Panel}. It encompasses
17 * both the grid header configuration as well as displaying data within the grid itself. If the
18 * {@link #columns} configuration is specified, this column will become a column group and can
19 * contain other columns inside. In general, this class will not be created directly, rather
20 * an array of column configurations will be passed to the grid:
23 * Ext.create('Ext.data.Store', {
24 * storeId:'employeeStore',
25 * fields:['firstname', 'lastname', 'senority', 'dep', 'hired'],
27 * {firstname:"Michael", lastname:"Scott", senority:7, dep:"Manangement", hired:"01/10/2004"},
28 * {firstname:"Dwight", lastname:"Schrute", senority:2, dep:"Sales", hired:"04/01/2004"},
29 * {firstname:"Jim", lastname:"Halpert", senority:3, dep:"Sales", hired:"02/22/2006"},
30 * {firstname:"Kevin", lastname:"Malone", senority:4, dep:"Accounting", hired:"06/10/2007"},
31 * {firstname:"Angela", lastname:"Martin", senority:5, dep:"Accounting", hired:"10/21/2008"}
35 * Ext.create('Ext.grid.Panel', {
36 * title: 'Column Demo',
37 * store: Ext.data.StoreManager.lookup('employeeStore'),
39 * {text: 'First Name', dataIndex:'firstname'},
40 * {text: 'Last Name', dataIndex:'lastname'},
41 * {text: 'Hired Month', dataIndex:'hired', xtype:'datecolumn', format:'M'},
42 * {text: 'Department (Yrs)', xtype:'templatecolumn', tpl:'{dep} ({senority})'}
45 * renderTo: Ext.getBody()
48 * # Convenience Subclasses
50 * There are several column subclasses that provide default rendering for various data types
52 * - {@link Ext.grid.column.Action}: Renders icons that can respond to click events inline
53 * - {@link Ext.grid.column.Boolean}: Renders for boolean values
54 * - {@link Ext.grid.column.Date}: Renders for date values
55 * - {@link Ext.grid.column.Number}: Renders for numeric values
56 * - {@link Ext.grid.column.Template}: Renders a value using an {@link Ext.XTemplate} using the record data
60 * The columns are laid out by a {@link Ext.layout.container.HBox} layout, so a column can either
61 * be given an explicit width value or a flex configuration. If no width is specified the grid will
62 * automatically the size the column to 100px. For column groups, the size is calculated by measuring
63 * the width of the child columns, so a width option should not be specified in that case.
67 * - {@link #text}: Sets the header text for the column
68 * - {@link #sortable}: Specifies whether the column can be sorted by clicking the header or using the column menu
69 * - {@link #hideable}: Specifies whether the column can be hidden using the column menu
70 * - {@link #menuDisabled}: Disables the column header menu
71 * - {@link #draggable}: Specifies whether the column header can be reordered by dragging
72 * - {@link #groupable}: Specifies whether the grid can be grouped by the column dataIndex. See also {@link Ext.grid.feature.Grouping}
76 * - {@link #dataIndex}: The dataIndex is the field in the underlying {@link Ext.data.Store} to use as the value for the column.
77 * - {@link #renderer}: Allows the underlying store value to be transformed before being displayed in the grid
79 Ext.define('Ext.grid.column.Column', {
80 extend: 'Ext.grid.header.Container',
81 alias: 'widget.gridcolumn',
82 requires: ['Ext.util.KeyNav'],
83 alternateClassName: 'Ext.grid.Column',
85 baseCls: Ext.baseCSSPrefix + 'column-header ' + Ext.baseCSSPrefix + 'unselectable',
87 // Not the standard, automatically applied overCls because we must filter out overs of child headers.
88 hoverCls: Ext.baseCSSPrefix + 'column-header-over',
94 possibleSortStates: ['ASC', 'DESC'],
97 '<div id="{id}-titleContainer" class="' + Ext.baseCSSPrefix + 'column-header-inner">' +
98 '<span id="{id}-textEl" class="' + Ext.baseCSSPrefix + 'column-header-text">' +
101 '<tpl if="!values.menuDisabled">'+
102 '<div id="{id}-triggerEl" class="' + Ext.baseCSSPrefix + 'column-header-trigger"></div>'+
107 * @cfg {Object[]} columns
108 * An optional array of sub-column definitions. This column becomes a group, and houses the columns defined in the
111 * Group columns may not be sortable. But they may be hideable and moveable. And you may move headers into and out
112 * of a group. Note that if all sub columns are dragged out of a group, the group is destroyed.
116 * @cfg {String} dataIndex
117 * The name of the field in the grid's {@link Ext.data.Store}'s {@link Ext.data.Model} definition from
118 * which to draw the column's value. **Required.**
124 * The header text to be used as innerHTML (html tags are accepted) to display in the Grid.
125 * **Note**: to have a clickable header with no text displayed you can use the default of ` ` aka ` `.
130 * @cfg {Boolean} sortable
131 * False to disable sorting of this column. Whether local/remote sorting is used is specified in
132 * `{@link Ext.data.Store#remoteSort}`. Defaults to true.
137 * @cfg {Boolean} groupable
138 * If the grid uses a {@link Ext.grid.feature.Grouping}, this option may be used to disable the header menu
139 * item to group by the column selected. By default, the header menu group option is enabled. Set to false to
140 * disable (but still show) the group option in the header menu for the column.
144 * @cfg {Boolean} fixed
146 * True to prevent the column from being resizable.
150 * @cfg {Boolean} resizable
151 * Set to <code>false</code> to prevent the column from being resizable. Defaults to <code>true</code>
155 * @cfg {Boolean} hideable
156 * False to prevent the user from hiding this column. Defaults to true.
161 * @cfg {Boolean} menuDisabled
162 * True to disable the column header menu containing sort/hide options. Defaults to false.
167 * @cfg {Function} renderer
168 * A renderer is an 'interceptor' method which can be used transform data (value, appearance, etc.)
169 * before it is rendered. Example:
172 * renderer: function(value){
176 * return value + ' people';
180 * @cfg {Object} renderer.value The data value for the current cell
181 * @cfg {Object} renderer.metaData A collection of metadata about the current cell; can be used or modified
182 * by the renderer. Recognized properties are: tdCls, tdAttr, and style.
183 * @cfg {Ext.data.Model} renderer.record The record for the current row
184 * @cfg {Number} renderer.rowIndex The index of the current row
185 * @cfg {Number} renderer.colIndex The index of the current column
186 * @cfg {Ext.data.Store} renderer.store The data store
187 * @cfg {Ext.view.View} renderer.view The current view
188 * @cfg {String} renderer.return The HTML string to be rendered.
193 * @cfg {String} align
194 * Sets the alignment of the header and rendered columns. Defaults to 'left'.
199 * @cfg {Boolean} draggable
200 * False to disable drag-drop reordering of this column. Defaults to true.
204 // Header does not use the typical ComponentDraggable class and therefore we
205 // override this with an emptyFn. It is controlled at the HeaderDragZone.
206 initDraggable: Ext.emptyFn,
209 * @cfg {String} tdCls
210 * A CSS class names to apply to the table cells for this column.
214 * @cfg {Object/String} editor
215 * An optional xtype or config object for a {@link Ext.form.field.Field Field} to use for editing.
216 * Only applicable if the grid is using an {@link Ext.grid.plugin.Editing Editing} plugin.
220 * @cfg {Object/String} field
221 * Alias for {@link #editor}.
222 * @deprecated 4.0.5 Use {@link #editor} instead.
226 * @property {Ext.Element} triggerEl
227 * Element that acts as button for column header dropdown menu.
231 * @property {Ext.Element} textEl
232 * Element that contains the text in column header.
237 * Set in this class to identify, at runtime, instances which are not instances of the
238 * HeaderContainer base class, but are in fact, the subclass: Header.
242 initComponent: function() {
248 if (Ext.isDefined(me.header)) {
253 // Flexed Headers need to have a minWidth defined so that they can never be squeezed out of existence by the
254 // HeaderContainer's specialized Box layout, the ColumnLayout. The ColumnLayout's overridden calculateChildboxes
255 // method extends the available layout space to accommodate the "desiredWidth" of all the columns.
257 me.minWidth = me.minWidth || Ext.grid.plugin.HeaderResizer.prototype.minColWidth;
259 // Non-flexed Headers may never be squeezed in the event of a shortfall so
260 // always set their minWidth to their current width.
262 me.minWidth = me.width;
265 if (!me.triStateSort) {
266 me.possibleSortStates.length = 2;
269 // A group header; It contains items which are themselves Headers
270 if (Ext.isDefined(me.columns)) {
271 me.isGroupHeader = true;
275 Ext.Error.raise('Ext.grid.column.Column: Group header may not accept a dataIndex');
277 if ((me.width && me.width !== Ext.grid.header.Container.prototype.defaultWidth) || me.flex) {
278 Ext.Error.raise('Ext.grid.column.Column: Group header does not support setting explicit widths or flexs. The group header width is calculated by the sum of its children.');
282 // The headers become child items
283 me.items = me.columns;
288 // Acquire initial width from sub headers
289 for (i = 0, len = me.items.length; i < len; i++) {
292 me.width += item.width || Ext.grid.header.Container.prototype.defaultWidth;
296 Ext.Error.raise('Ext.grid.column.Column: items of a grouped header do not support flexed values. Each item must explicitly define its width.');
300 me.minWidth = me.width;
302 me.cls = (me.cls||'') + ' ' + Ext.baseCSSPrefix + 'group-header';
304 me.resizable = false;
308 me.addChildEls('titleContainer', 'triggerEl', 'textEl');
310 // Initialize as a HeaderContainer
311 me.callParent(arguments);
314 onAdd: function(childHeader) {
315 childHeader.isSubHeader = true;
316 childHeader.addCls(Ext.baseCSSPrefix + 'group-sub-header');
317 this.callParent(arguments);
320 onRemove: function(childHeader) {
321 childHeader.isSubHeader = false;
322 childHeader.removeCls(Ext.baseCSSPrefix + 'group-sub-header');
323 this.callParent(arguments);
326 initRenderData: function() {
329 Ext.applyIf(me.renderData, {
331 menuDisabled: me.menuDisabled
333 return me.callParent(arguments);
336 applyColumnState: function (state) {
338 defined = Ext.isDefined;
341 me.applyColumnsState(state.columns);
343 // Only state properties which were saved should be restored.
344 // (Only user-changed properties were saved by getState)
345 if (defined(state.hidden)) {
346 me.hidden = state.hidden;
348 if (defined(state.locked)) {
349 me.locked = state.locked;
351 if (defined(state.sortable)) {
352 me.sortable = state.sortable;
354 if (defined(state.width)) {
356 me.width = state.width;
357 } else if (defined(state.flex)) {
359 me.flex = state.flex;
363 getColumnState: function () {
370 me.savePropsToState(['hidden', 'sortable', 'locked', 'flex', 'width'], state);
372 if (me.isGroupHeader) {
373 me.items.each(function(column){
374 columns.push(column.getColumnState());
376 if (columns.length) {
377 state.columns = columns;
379 } else if (me.isSubHeader && me.ownerCt.hidden) {
380 // don't set hidden on the children so they can auto height
384 if ('width' in state) {
385 delete state.flex; // width wins
391 * Sets the header text for this Column.
392 * @param {String} text The header to display on this Column.
394 setText: function(text) {
397 this.textEl.update(text);
401 // Find the topmost HeaderContainer: An ancestor which is NOT a Header.
402 // Group Headers are themselves HeaderContainers
403 getOwnerHeaderCt: function() {
404 return this.up(':not([isHeader])');
408 * Returns the true grid column index associated with this column only if this column is a base level Column. If it
409 * is a group column, it returns `false`.
412 getIndex: function() {
413 return this.isGroupColumn ? false : this.getOwnerHeaderCt().getHeaderIndex(this);
416 onRender: function() {
418 grid = me.up('tablepanel');
420 // Disable the menu if there's nothing to show in the menu, ie:
421 // Column cannot be sorted, grouped or locked, and there are no grid columns which may be hidden
422 if (grid && (!me.sortable || grid.sortableColumns === false) && !me.groupable && !me.lockable && (grid.enableColumnHide === false || !me.getOwnerHeaderCt().getHideableColumns().length)) {
423 me.menuDisabled = true;
425 me.callParent(arguments);
428 afterRender: function() {
432 me.callParent(arguments);
434 el.addCls(Ext.baseCSSPrefix + 'column-header-align-' + me.align).addClsOnOver(me.overCls);
438 dblclick: me.onElDblClick,
442 // BrowserBug: Ie8 Strict Mode, this will break the focus for this browser,
443 // must be fixed when focus management will be implemented.
444 if (!Ext.isIE8 || !Ext.isStrict) {
445 me.mon(me.getFocusEl(), {
446 focus: me.onTitleMouseOver,
447 blur: me.onTitleMouseOut,
452 me.mon(me.titleContainer, {
453 mouseenter: me.onTitleMouseOver,
454 mouseleave: me.onTitleMouseOut,
458 me.keyNav = Ext.create('Ext.util.KeyNav', el, {
459 enter: me.onEnterKey,
466 * Sets the width of this Column.
467 * @param {Number} width New width.
469 setWidth: function(width, /* private - used internally */ doLayout) {
471 headerCt = me.ownerCt,
474 oldWidth = me.getWidth(),
478 if (width !== oldWidth) {
479 me.oldWidth = oldWidth;
481 // Non-flexed Headers may never be squeezed in the event of a shortfall so
482 // always set the minWidth to their current width.
483 me.minWidth = me.width = width;
485 // Bubble size changes upwards to group headers
486 if (headerCt.isGroupHeader) {
487 siblings = headerCt.items.items;
488 len = siblings.length;
490 for (i = 0; i < len; i++) {
491 sibling = siblings[i];
492 if (!sibling.hidden) {
493 groupWidth += (sibling === me) ? width : sibling.getWidth();
496 headerCt.setWidth(groupWidth, doLayout);
497 } else if (doLayout !== false) {
498 // Allow the owning Container to perform the sizing
504 afterComponentLayout: function(width, height) {
506 ownerHeaderCt = this.getOwnerHeaderCt();
508 me.callParent(arguments);
510 // Only changes at the base level inform the grid's HeaderContainer which will update the View
511 // Skip this if the width is null or undefined which will be the Box layout's initial pass through the child Components
512 // Skip this if it's the initial size setting in which case there is no ownerheaderCt yet - that is set afterRender
513 if (width && !me.isGroupHeader && ownerHeaderCt) {
514 ownerHeaderCt.onHeaderResize(me, width, true);
516 if (me.oldWidth && (width !== me.oldWidth)) {
517 ownerHeaderCt.fireEvent('columnresize', ownerHeaderCt, this, width);
523 // After the container has laid out and stretched, it calls this to correctly pad the inner to center the text vertically
524 // Total available header height must be passed to enable padding for inner elements to be calculated.
525 setPadding: function(headerHeight) {
527 lineHeight = Ext.util.TextMetrics.measure(me.textEl.dom, me.text).height;
529 // Top title containing element must stretch to match height of sibling group headers
530 if (!me.isGroupHeader) {
531 if (me.titleContainer.getHeight() < headerHeight) {
532 me.titleContainer.dom.style.height = headerHeight + 'px';
535 headerHeight = me.titleContainer.getViewSize().height;
537 // Vertically center the header text in potentially vertically stretched header
539 me.titleContainer.setStyle({
540 paddingTop: Math.max(((headerHeight - lineHeight) / 2), 0) + 'px'
544 // Only IE needs this
545 if (Ext.isIE && me.triggerEl) {
546 me.triggerEl.setHeight(headerHeight);
550 onDestroy: function() {
552 // force destroy on the textEl, IE reports a leak
553 Ext.destroy(me.textEl, me.keyNav);
555 me.callParent(arguments);
558 onTitleMouseOver: function() {
559 this.titleContainer.addCls(this.hoverCls);
562 onTitleMouseOut: function() {
563 this.titleContainer.removeCls(this.hoverCls);
566 onDownKey: function(e) {
567 if (this.triggerEl) {
568 this.onElClick(e, this.triggerEl.dom || this.el.dom);
572 onEnterKey: function(e) {
573 this.onElClick(e, this.el.dom);
582 onElDblClick: function(e, t) {
584 ownerCt = me.ownerCt;
585 if (ownerCt && Ext.Array.indexOf(ownerCt.items, me) !== 0 && me.isOnLeftEdge(e) ) {
586 ownerCt.expandToFit(me.previousSibling('gridcolumn'));
590 onElClick: function(e, t) {
592 // The grid's docked HeaderContainer.
594 ownerHeaderCt = me.getOwnerHeaderCt();
596 if (ownerHeaderCt && !ownerHeaderCt.ddLock) {
597 // Firefox doesn't check the current target in a within check.
598 // Therefore we check the target directly and then within (ancestors)
599 if (me.triggerEl && (e.target === me.triggerEl.dom || t === me.triggerEl.dom || e.within(me.triggerEl))) {
600 ownerHeaderCt.onHeaderTriggerClick(me, e, t);
601 // if its not on the left hand edge, sort
602 } else if (e.getKey() || (!me.isOnLeftEdge(e) && !me.isOnRightEdge(e))) {
603 me.toggleSortState();
604 ownerHeaderCt.onHeaderClick(me, e, t);
611 * Process UI events from the view. The owning TablePanel calls this method, relaying events from the TableView
612 * @param {String} type Event type, eg 'click'
613 * @param {Ext.view.Table} view TableView Component
614 * @param {HTMLElement} cell Cell HtmlElement the event took place within
615 * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
616 * @param {Number} cellIndex Cell index within the row
617 * @param {Ext.EventObject} e Original event
619 processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
620 return this.fireEvent.apply(this, arguments);
623 toggleSortState: function() {
629 idx = Ext.Array.indexOf(me.possibleSortStates, me.sortState);
631 nextIdx = (idx + 1) % me.possibleSortStates.length;
632 me.setSortState(me.possibleSortStates[nextIdx]);
636 doSort: function(state) {
637 var ds = this.up('tablepanel').store;
639 property: this.getSortParam(),
645 * Returns the parameter to sort upon when sorting this header. By default this returns the dataIndex and will not
646 * need to be overriden in most cases.
649 getSortParam: function() {
650 return this.dataIndex;
653 //setSortState: function(state, updateUI) {
654 //setSortState: function(state, doSort) {
655 setSortState: function(state, skipClear, initial) {
657 colSortClsPrefix = Ext.baseCSSPrefix + 'column-header-sort-',
658 ascCls = colSortClsPrefix + 'ASC',
659 descCls = colSortClsPrefix + 'DESC',
660 nullCls = colSortClsPrefix + 'null',
661 ownerHeaderCt = me.getOwnerHeaderCt(),
662 oldSortState = me.sortState;
664 if (oldSortState !== state && me.getSortParam()) {
665 me.addCls(colSortClsPrefix + state);
666 // don't trigger a sort on the first time, we just want to update the UI
667 if (state && !initial) {
672 me.removeCls([ascCls, nullCls]);
675 me.removeCls([descCls, nullCls]);
678 me.removeCls([ascCls, descCls]);
681 if (ownerHeaderCt && !me.triStateSort && !skipClear) {
682 ownerHeaderCt.clearOtherSortStates(me);
684 me.sortState = state;
685 ownerHeaderCt.fireEvent('sortchange', ownerHeaderCt, me, state);
695 ownerHeaderCt = me.getOwnerHeaderCt();
697 // Hiding means setting to zero width, so cache the width
698 me.oldWidth = me.getWidth();
700 // Hiding a group header hides itself, and then informs the HeaderContainer about its sub headers (Suppressing header layout)
701 if (me.isGroupHeader) {
702 items = me.items.items;
703 me.callParent(arguments);
704 ownerHeaderCt.onHeaderHide(me);
705 for (i = 0, len = items.length; i < len; i++) {
706 items[i].hidden = true;
707 ownerHeaderCt.onHeaderHide(items[i], true);
712 // TODO: Work with Jamie to produce a scheme where we can show/hide/resize without triggering a layout cascade
713 lb = me.ownerCt.componentLayout.layoutBusy;
714 me.ownerCt.componentLayout.layoutBusy = true;
715 me.callParent(arguments);
716 me.ownerCt.componentLayout.layoutBusy = lb;
718 // Notify owning HeaderContainer
719 ownerHeaderCt.onHeaderHide(me);
721 if (me.ownerCt.isGroupHeader) {
722 // If we've just hidden the last header in a group, then hide the group
723 items = me.ownerCt.query('>:not([hidden])');
727 // Size the group down to accommodate fewer sub headers
729 for (i = 0, len = items.length; i < len; i++) {
730 newWidth += items[i].getWidth();
732 me.ownerCt.minWidth = newWidth;
733 me.ownerCt.setWidth(newWidth);
740 ownerCt = me.ownerCt,
741 ownerCtCompLayout = ownerCt.componentLayout,
742 ownerCtCompLayoutBusy = ownerCtCompLayout.layoutBusy,
743 ownerCtLayout = ownerCt.layout,
744 ownerCtLayoutBusy = ownerCtLayout.layoutBusy,
750 // TODO: Work with Jamie to produce a scheme where we can show/hide/resize without triggering a layout cascade
752 // Suspend our owner's layouts (both component and container):
753 ownerCtCompLayout.layoutBusy = ownerCtLayout.layoutBusy = true;
755 me.callParent(arguments);
757 ownerCtCompLayout.layoutBusy = ownerCtCompLayoutBusy;
758 ownerCtLayout.layoutBusy = ownerCtLayoutBusy;
760 // If a sub header, ensure that the group header is visible
761 if (me.isSubHeader) {
762 if (!ownerCt.isVisible()) {
767 // If we've just shown a group with all its sub headers hidden, then show all its sub headers
768 if (me.isGroupHeader && !me.query(':not([hidden])').length) {
769 items = me.query('>*');
770 for (i = 0, len = items.length; i < len; i++) {
772 item.preventLayout = true;
774 newWidth += item.getWidth();
775 delete item.preventLayout;
777 me.setWidth(newWidth);
780 // Resize the owning group to accommodate
781 if (ownerCt.isGroupHeader && me.preventLayout !== true) {
782 items = ownerCt.query('>:not([hidden])');
783 for (i = 0, len = items.length; i < len; i++) {
784 newWidth += items[i].getWidth();
786 ownerCt.minWidth = newWidth;
787 ownerCt.setWidth(newWidth);
790 // Notify owning HeaderContainer
791 ownerCt = me.getOwnerHeaderCt();
793 ownerCt.onHeaderShow(me, me.preventLayout);
797 getDesiredWidth: function() {
799 if (me.rendered && me.componentLayout && me.componentLayout.lastComponentSize) {
800 // headers always have either a width or a flex
801 // because HeaderContainer sets a defaults width
802 // therefore we can ignore the natural width
803 // we use the componentLayout's tracked width so that
804 // we can calculate the desired width when rendered
805 // but not visible because its being obscured by a layout
806 return me.componentLayout.lastComponentSize.width;
807 // Flexed but yet to be rendered this could be the case
808 // where a HeaderContainer and Headers are simply used as data
809 // structures and not rendered.
812 // this is going to be wrong, the defaultWidth
820 getCellSelector: function() {
821 return '.' + Ext.baseCSSPrefix + 'grid-cell-' + this.getItemId();
824 getCellInnerSelector: function() {
825 return this.getCellSelector() + ' .' + Ext.baseCSSPrefix + 'grid-cell-inner';
828 isOnLeftEdge: function(e) {
829 return (e.getXY()[0] - this.el.getLeft() <= this.handleWidth);
832 isOnRightEdge: function(e) {
833 return (this.el.getRight() - e.getXY()[0] <= this.handleWidth);
836 // intentionally omit getEditor and setEditor definitions bc we applyIf into columns
837 // when the editing plugin is injected
841 * Retrieves the editing field for editing associated with this header. Returns false if there is no field
842 * associated with the Header the method will return false. If the field has not been instantiated it will be
843 * created. Note: These methods only has an implementation if a Editing plugin has been enabled on the grid.
844 * @param {Object} record The {@link Ext.data.Model Model} instance being edited.
845 * @param {Object} defaultField An object representing a default field to be created
846 * @return {Ext.form.field.Field} field
850 * Sets the form field to be used for editing. Note: This method only has an implementation if an Editing plugin has
851 * been enabled on the grid.
852 * @param {Object} field An object representing a field to be created. If no xtype is specified a 'textfield' is