X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/3789b528d8dd8aad4558e38e22d775bcab1cbd36..6746dc89c47ed01b165cc1152533605f97eb8e8d:/docs/source/Column2.html diff --git a/docs/source/Column2.html b/docs/source/Column2.html index 97001617..30f4b986 100644 --- a/docs/source/Column2.html +++ b/docs/source/Column2.html @@ -15,190 +15,746 @@
-/** - * @class Ext.layout.container.Column - * @extends Ext.layout.container.Auto - * <p>This is the layout style of choice for creating structural layouts in a multi-column format where the width of - * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content. - * This class is intended to be extended or created via the layout:'column' {@link Ext.container.Container#layout} config, - * and should generally not need to be created directly via the new keyword.</p> - * <p>ColumnLayout does not have any direct config options (other than inherited ones), but it does support a - * specific config property of <b><tt>columnWidth</tt></b> that can be included in the config of any panel added to it. The - * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel. - * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).</p> - * <p>The width property is always evaluated as pixels, and must be a number greater than or equal to 1. - * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and - * less than 1 (e.g., .25).</p> - * <p>The basic rules for specifying column widths are pretty simple. The logic makes two passes through the - * set of contained panels. During the first layout pass, all panels that either have a fixed width or none - * specified (auto) are skipped, but their widths are subtracted from the overall container width. During the second - * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on - * the total <b>remaining</b> container width. In other words, percentage width panels are designed to fill the space - * left over by all the fixed-width and/or auto-width panels. Because of this, while you can specify any number of columns - * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your - * layout may not render as expected. - * {@img Ext.layout.container.Column/Ext.layout.container.Column1.png Ext.layout.container.Column container layout} - * Example usage:</p> - * <pre><code> - // All columns are percentages -- they must add up to 1 - Ext.create('Ext.panel.Panel', { - title: 'Column Layout - Percentage Only', - width: 350, - height: 250, - layout:'column', - items: [{ - title: 'Column 1', - columnWidth: .25 - },{ - title: 'Column 2', - columnWidth: .55 - },{ - title: 'Column 3', - columnWidth: .20 - }], - renderTo: Ext.getBody() - }); - -// {@img Ext.layout.container.Column/Ext.layout.container.Column2.png Ext.layout.container.Column container layout} -// Mix of width and columnWidth -- all columnWidth values must add up -// to 1. The first column will take up exactly 120px, and the last two -// columns will fill the remaining container width. - - Ext.create('Ext.Panel', { - title: 'Column Layout - Mixed', - width: 350, - height: 250, - layout:'column', - items: [{ - title: 'Column 1', - width: 120 - },{ - title: 'Column 2', - columnWidth: .7 - },{ - title: 'Column 3', - columnWidth: .3 - }], - renderTo: Ext.getBody() - }); -</code></pre> +/** + * @class Ext.grid.column.Column + * @extends Ext.grid.header.Container + * + * This class specifies the definition for a column inside a {@link Ext.grid.Panel}. It encompasses + * both the grid header configuration as well as displaying data within the grid itself. If the + * {@link #columns} configuration is specified, this column will become a column group and can + * container other columns inside. In general, this class will not be created directly, rather + * an array of column configurations will be passed to the grid: + * + * {@img Ext.grid.column.Column/Ext.grid.column.Column.png Ext.grid.column.Column grid column} + * + * ## Code + * + * Ext.create('Ext.data.Store', { + * storeId:'employeeStore', + * fields:['firstname', 'lastname', 'senority', 'dep', 'hired'], + * data:[ + * {firstname:"Michael", lastname:"Scott", senority:7, dep:"Manangement", hired:"01/10/2004"}, + * {firstname:"Dwight", lastname:"Schrute", senority:2, dep:"Sales", hired:"04/01/2004"}, + * {firstname:"Jim", lastname:"Halpert", senority:3, dep:"Sales", hired:"02/22/2006"}, + * {firstname:"Kevin", lastname:"Malone", senority:4, dep:"Accounting", hired:"06/10/2007"}, + * {firstname:"Angela", lastname:"Martin", senority:5, dep:"Accounting", hired:"10/21/2008"} + * ] + * }); + * + * Ext.create('Ext.grid.Panel', { + * title: 'Column Demo', + * store: Ext.data.StoreManager.lookup('employeeStore'), + * columns: [ + * {text: 'First Name', dataIndex:'firstname'}, + * {text: 'Last Name', dataIndex:'lastname'}, + * {text: 'Hired Month', dataIndex:'hired', xtype:'datecolumn', format:'M'}, + * {text: 'Deparment (Yrs)', xtype:'templatecolumn', tpl:'{dep} ({senority})'} + * ], + * width: 400, + * renderTo: Ext.getBody() + * }); + * + * ## Convenience Subclasses + * There are several column subclasses that provide default rendering for various data types + * + * - {@link Ext.grid.column.Action}: Renders icons that can respond to click events inline + * - {@link Ext.grid.column.Boolean}: Renders for boolean values + * - {@link Ext.grid.column.Date}: Renders for date values + * - {@link Ext.grid.column.Number}: Renders for numeric values + * - {@link Ext.grid.column.Template}: Renders a value using an {@link Ext.XTemplate} using the record data + * + * ## Setting Sizes + * The columns are laid out by a {@link Ext.layout.container.HBox} layout, so a column can either + * be given an explicit width value or a flex configuration. If no width is specified the grid will + * automatically the size the column to 100px. For column groups, the size is calculated by measuring + * the width of the child columns, so a width option should not be specified in that case. + * + * ## Header Options + * - {@link #text}: Sets the header text for the column + * - {@link #sortable}: Specifies whether the column can be sorted by clicking the header or using the column menu + * - {@link #hideable}: Specifies whether the column can be hidden using the column menu + * - {@link #menuDisabled}: Disables the column header menu + * - {@link #draggable}: Specifies whether the column header can be reordered by dragging + * - {@link #groupable}: Specifies whether the grid can be grouped by the column dataIndex. See also {@link Ext.grid.feature.Grouping} + * + * ## Data Options + * - {@link #dataIndex}: The dataIndex is the field in the underlying {@link Ext.data.Store} to use as the value for the column. + * - {@link #renderer}: Allows the underlying store value to be transformed before being displayed in the grid */ -Ext.define('Ext.layout.container.Column', { +Ext.define('Ext.grid.column.Column', { + extend: 'Ext.grid.header.Container', + alias: 'widget.gridcolumn', + requires: ['Ext.util.KeyNav'], + alternateClassName: 'Ext.grid.Column', - extend: 'Ext.layout.container.Auto', - alias: ['layout.column'], - alternateClassName: 'Ext.layout.ColumnLayout', + baseCls: Ext.baseCSSPrefix + 'column-header ' + Ext.baseCSSPrefix + 'unselectable', - type: 'column', + // Not the standard, automatically applied overCls because we must filter out overs of child headers. + hoverCls: Ext.baseCSSPrefix + 'column-header-over', - itemCls: Ext.baseCSSPrefix + 'column', + handleWidth: 5, - targetCls: Ext.baseCSSPrefix + 'column-layout-ct', + sortState: null, - scrollOffset: 0, + possibleSortStates: ['ASC', 'DESC'], - bindToOwnerCtComponent: false, + renderTpl: + '<div class="' + Ext.baseCSSPrefix + 'column-header-inner">' + + '<span class="' + Ext.baseCSSPrefix + 'column-header-text">' + + '{text}' + + '</span>' + + '<tpl if="!values.menuDisabled"><div class="' + Ext.baseCSSPrefix + 'column-header-trigger"></div></tpl>' + + '</div>', - getRenderTarget : function() { - if (!this.innerCt) { + /** + * @cfg {Array} columns + * <p>An optional array of sub-column definitions. This column becomes a group, and houses the columns defined in the <code>columns</code> config.</p> + * <p>Group columns may not be sortable. But they may be hideable and moveable. And you may move headers into and out of a group. Note that + * if all sub columns are dragged out of a group, the group is destroyed. + */ - // the innerCt prevents wrapping and shuffling while - // the container is resizing - this.innerCt = this.getTarget().createChild({ - cls: Ext.baseCSSPrefix + 'column-inner' - }); + /** + * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the + * grid's {@link Ext.data.Store}'s {@link Ext.data.Model} definition from + * which to draw the column's value.</p> + */ + dataIndex: null, + + /** + * @cfg {String} text Optional. The header text to be used as innerHTML + * (html tags are accepted) to display in the Grid. <b>Note</b>: to + * have a clickable header with no text displayed you can use the + * default of <tt>' '</tt>. + */ + text: ' ', + + /** + * @cfg {Boolean} sortable Optional. <tt>true</tt> if sorting is to be allowed on this column. + * Whether local/remote sorting is used is specified in <code>{@link Ext.data.Store#remoteSort}</code>. + */ + sortable: true, + + /** + * @cfg {Boolean} groupable Optional. If the grid uses a {@link Ext.grid.feature.Grouping}, this option + * may be used to disable the header menu item to group by the column selected. By default, + * the header menu group option is enabled. Set to false to disable (but still show) the + * group option in the header menu for the column. + */ + + /** + * @cfg {Boolean} fixed Prevents the column from being resizable + */ + + /** + * @cfg {Boolean} resizable This config has no effect on a grid column, please see {@link #fixed} instead. + */ + + /** + * @cfg {Boolean} hideable Optional. Specify as <tt>false</tt> to prevent the user from hiding this column + * (defaults to true). + */ + hideable: true, + + * @cfg {Boolean} menuDisabled + * True to disabled the column header menu containing sort/hide options. Defaults to false. + */ + menuDisabled: false, + + /** + * @method + * <p>A renderer is an 'interceptor' method which can be used transform data (value, appearance, etc.) before it + * is rendered. Example:</p> + * <pre><code>{ + renderer: function(value){ + if (value === 1) { + return '1 person'; + } + return value + ' people'; + } +} + * </code></pre> + * @param {Mixed} value The data value for the current cell + * @param {Object} metaData A collection of metadata about the current cell; can be used or modified by + * the renderer. Recognized properties are: <tt>tdCls</tt>, <tt>tdAttr</tt>, and <tt>style</tt>. + * @param {Ext.data.Model} record The record for the current row + * @param {Number} rowIndex The index of the current row + * @param {Number} colIndex The index of the current column + * @param {Ext.data.Store} store The data store + * @param {Ext.view.View} view The current view + * @return {String} The HTML to be rendered + */ + renderer: false, + + /** + * @cfg {String} align Sets the alignment of the header and rendered columns. + * Defaults to 'left'. + */ + align: 'left', + + /** + * @cfg {Boolean} draggable Indicates whether or not the header can be drag and drop re-ordered. + * Defaults to true. + */ + draggable: true, + + // Header does not use the typical ComponentDraggable class and therefore we + // override this with an emptyFn. It is controlled at the HeaderDragZone. + initDraggable: Ext.emptyFn, + + /** + * @cfg {String} tdCls <p>Optional. A CSS class names to apply to the table cells for this column.</p> + */ + + /** + * @property {Ext.core.Element} triggerEl + */ + + /** + * @property {Ext.core.Element} textEl + */ + + /** + * @private + * Set in this class to identify, at runtime, instances which are not instances of the + * HeaderContainer base class, but are in fact, the subclass: Header. + */ + isHeader: true, + + initComponent: function() { + var me = this, + i, + len; + + if (Ext.isDefined(me.header)) { + me.text = me.header; + delete me.header; + } + + // Flexed Headers need to have a minWidth defined so that they can never be squeezed out of existence by the + // HeaderContainer's specialized Box layout, the ColumnLayout. The ColumnLayout's overridden calculateChildboxes + // method extends the available layout space to accommodate the "desiredWidth" of all the columns. + if (me.flex) { + me.minWidth = me.minWidth || Ext.grid.plugin.HeaderResizer.prototype.minColWidth; + } + // Non-flexed Headers may never be squeezed in the event of a shortfall so + // always set their minWidth to their current width. + else { + me.minWidth = me.width; + } + + if (!me.triStateSort) { + me.possibleSortStates.length = 2; + } + + // A group header; It contains items which are themselves Headers + if (Ext.isDefined(me.columns)) { + me.isGroupHeader = true; + + //<debug> + if (me.dataIndex) { + Ext.Error.raise('Ext.grid.column.Column: Group header may not accept a dataIndex'); + } + if ((me.width && me.width !== Ext.grid.header.Container.prototype.defaultWidth) || me.flex) { + 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.'); + } + //</debug> + + // The headers become child items + me.items = me.columns; + delete me.columns; + delete me.flex; + me.width = 0; + + // Acquire initial width from sub headers + for (i = 0, len = me.items.length; i < len; i++) { + me.width += me.items[i].width || Ext.grid.header.Container.prototype.defaultWidth; + //<debug> + if (me.items[i].flex) { + Ext.Error.raise('Ext.grid.column.Column: items of a grouped header do not support flexed values. Each item must explicitly define its width.'); + } + //</debug> + } + me.minWidth = me.width; + + me.cls = (me.cls||'') + ' ' + Ext.baseCSSPrefix + 'group-header'; + me.sortable = false; + me.fixed = true; + me.align = 'center'; + } + + Ext.applyIf(me.renderSelectors, { + titleContainer: '.' + Ext.baseCSSPrefix + 'column-header-inner', + triggerEl: '.' + Ext.baseCSSPrefix + 'column-header-trigger', + textEl: '.' + Ext.baseCSSPrefix + 'column-header-text' + }); + + // Initialize as a HeaderContainer + me.callParent(arguments); + }, - // Column layout uses natural HTML flow to arrange the child items. - // To ensure that all browsers (I'm looking at you IE!) add the bottom margin of the last child to the - // containing element height, we create a zero-sized element with style clear:both to force a "new line" - this.clearEl = this.innerCt.createChild({ - cls: Ext.baseCSSPrefix + 'clear', - role: 'presentation' + onAdd: function(childHeader) { + childHeader.isSubHeader = true; + childHeader.addCls(Ext.baseCSSPrefix + 'group-sub-header'); + }, + + onRemove: function(childHeader) { + childHeader.isSubHeader = false; + childHeader.removeCls(Ext.baseCSSPrefix + 'group-sub-header'); + }, + + initRenderData: function() { + var me = this; + + Ext.applyIf(me.renderData, { + text: me.text, + menuDisabled: me.menuDisabled + }); + return me.callParent(arguments); + }, + + /** + * Sets the header text for this Column. + * @param text The header to display on this Column. + */ + setText: function(text) { + this.text = text; + if (this.rendered) { + this.textEl.update(text); + } + }, + + // Find the topmost HeaderContainer: An ancestor which is NOT a Header. + // Group Headers are themselves HeaderContainers + getOwnerHeaderCt: function() { + return this.up(':not([isHeader])'); + }, + + /** + * Returns the true grid column index assiciated with this Column only if this column is a base level Column. + * If it is a group column, it returns <code>false</code> + */ + getIndex: function() { + return this.isGroupColumn ? false : this.getOwnerHeaderCt().getHeaderIndex(this); + }, + + afterRender: function() { + var me = this, + el = me.el; + + me.callParent(arguments); + + el.addCls(Ext.baseCSSPrefix + 'column-header-align-' + me.align).addClsOnOver(me.overCls); + + me.mon(el, { + click: me.onElClick, + dblclick: me.onElDblClick, + scope: me + }); + + // BrowserBug: Ie8 Strict Mode, this will break the focus for this browser, + // must be fixed when focus management will be implemented. + if (!Ext.isIE8 || !Ext.isStrict) { + me.mon(me.getFocusEl(), { + focus: me.onTitleMouseOver, + blur: me.onTitleMouseOut, + scope: me }); } - return this.innerCt; + + me.mon(me.titleContainer, { + mouseenter: me.onTitleMouseOver, + mouseleave: me.onTitleMouseOut, + scope: me + }); + + me.keyNav = Ext.create('Ext.util.KeyNav', el, { + enter: me.onEnterKey, + down: me.onDownKey, + scope: me + }); + }, + + setSize: function(width, height) { + var me = this, + headerCt = me.ownerCt, + ownerHeaderCt = me.getOwnerHeaderCt(), + siblings, + len, i, + oldWidth = me.getWidth(), + newWidth = 0, + readyForSizing = true, + hidden, + sibling; + + if (width !== oldWidth) { + + // Bubble size changes upwards to group headers + if (headerCt.isGroupHeader) { + siblings = headerCt.items.items; + len = siblings.length; + + /* + * setSize will be called for each column as it's rendered + * so we want to wait until all sub columns have been rendered + * before we try and calculate the size of the outer container. + * We also take into account hidden columns, because they won't + * be rendered, but we'll still need to make the calculation. + */ + for (i = 0; i < len; i++) { + sibling = siblings[i]; + hidden = sibling.hidden; + if (!sibling.rendered && !hidden) { + readyForSizing = false; + break; + } + if (!hidden) { + newWidth += (sibling === me) ? width : sibling.getWidth(); + } + } + + if (readyForSizing) { + headerCt.minWidth = newWidth; + headerCt.setWidth(newWidth); + } + } + me.callParent(arguments); + } + }, + + afterComponentLayout: function(width, height) { + var me = this, + ownerHeaderCt = this.getOwnerHeaderCt(); + + me.callParent(arguments); + + // Only changes at the base level inform the grid's HeaderContainer which will update the View + // Skip this if the width is null or undefined which will be the Box layout's initial pass through the child Components + // Skip this if it's the initial size setting in which case there is no ownerheaderCt yet - that is set afterRender + if (width && !me.isGroupHeader && ownerHeaderCt) { + ownerHeaderCt.onHeaderResize(me, width, true); + } }, // private - onLayout : function() { + // After the container has laid out and stretched, it calls this to correctly pad the inner to center the text vertically + setPadding: function() { var me = this, - target = me.getTarget(), - items = me.getLayoutItems(), - len = items.length, - item, - i, - parallelMargins = [], - itemParallelMargins, - size, - availableWidth, - columnWidth; - - size = me.getLayoutTargetSize(); - if (size.width < len * 10) { // Don't lay out in impossibly small target (probably display:none, or initial, unsized Container) - return; + headerHeight, + lineHeight = parseInt(me.textEl.getStyle('line-height'), 10); + + // Top title containing element must stretch to match height of sibling group headers + if (!me.isGroupHeader) { + headerHeight = me.el.getViewSize().height; + if (me.titleContainer.getHeight() < headerHeight) { + me.titleContainer.dom.style.height = headerHeight + 'px'; + } + } + headerHeight = me.titleContainer.getViewSize().height; + + // Vertically center the header text in potentially vertically stretched header + if (lineHeight) { + me.titleContainer.setStyle({ + paddingTop: Math.max(((headerHeight - lineHeight) / 2), 0) + 'px' + }); + } + + // Only IE needs this + if (Ext.isIE && me.triggerEl) { + me.triggerEl.setHeight(headerHeight); + } + }, + + onDestroy: function() { + var me = this; + Ext.destroy(me.keyNav); + delete me.keyNav; + me.callParent(arguments); + }, + + onTitleMouseOver: function() { + this.titleContainer.addCls(this.hoverCls); + }, + + onTitleMouseOut: function() { + this.titleContainer.removeCls(this.hoverCls); + }, + + onDownKey: function(e) { + if (this.triggerEl) { + this.onElClick(e, this.triggerEl.dom || this.el.dom); + } + }, + + onEnterKey: function(e) { + this.onElClick(e, this.el.dom); + }, + + /** + * @private + * Double click + * @param e + * @param t + */ + onElDblClick: function(e, t) { + var me = this, + ownerCt = me.ownerCt; + if (ownerCt && Ext.Array.indexOf(ownerCt.items, me) !== 0 && me.isOnLeftEdge(e) ) { + ownerCt.expandToFit(me.previousSibling('gridcolumn')); } + }, + + onElClick: function(e, t) { - // On the first pass, for all except IE6-7, we lay out the items with no scrollbars visible using style overflow: hidden. - // If, after the layout, it is detected that there is vertical overflow, - // we will recurse back through here. Do not adjust overflow style at that time. - if (me.adjustmentPass) { - if (Ext.isIE6 || Ext.isIE7 || Ext.isIEQuirks) { - size.width = me.adjustedWidth; + // The grid's docked HeaderContainer. + var me = this, + ownerHeaderCt = me.getOwnerHeaderCt(); + + if (ownerHeaderCt && !ownerHeaderCt.ddLock) { + // Firefox doesn't check the current target in a within check. + // Therefore we check the target directly and then within (ancestors) + if (me.triggerEl && (e.target === me.triggerEl.dom || t === me.triggerEl.dom || e.within(me.triggerEl))) { + ownerHeaderCt.onHeaderTriggerClick(me, e, t); + // if its not on the left hand edge, sort + } else if (e.getKey() || (!me.isOnLeftEdge(e) && !me.isOnRightEdge(e))) { + me.toggleSortState(); + ownerHeaderCt.onHeaderClick(me, e, t); } - } else { - i = target.getStyle('overflow'); - if (i && i != 'hidden') { - me.autoScroll = true; - if (!(Ext.isIE6 || Ext.isIE7 || Ext.isIEQuirks)) { - target.setStyle('overflow', 'hidden'); - size = me.getLayoutTargetSize(); - } + } + }, + + /** + * @private + * Process UI events from the view. The owning TablePanel calls this method, relaying events from the TableView + * @param {String} type Event type, eg 'click' + * @param {TableView} view TableView Component + * @param {HtmlElement} cell Cell HtmlElement the event took place within + * @param {Number} recordIndex Index of the associated Store Model (-1 if none) + * @param {Number} cellIndex Cell index within the row + * @param {EventObject} e Original event + */ + processEvent: function(type, view, cell, recordIndex, cellIndex, e) { + return this.fireEvent.apply(this, arguments); + }, + + toggleSortState: function() { + var me = this, + idx, + nextIdx; + + if (me.sortable) { + idx = Ext.Array.indexOf(me.possibleSortStates, me.sortState); + + nextIdx = (idx + 1) % me.possibleSortStates.length; + me.setSortState(me.possibleSortStates[nextIdx]); + } + }, + + doSort: function(state) { + var ds = this.up('tablepanel').store; + ds.sort({ + property: this.getSortParam(), + direction: state + }); + }, + + /** + * Returns the parameter to sort upon when sorting this header. By default + * this returns the dataIndex and will not need to be overriden in most cases. + */ + getSortParam: function() { + return this.dataIndex; + }, + + //setSortState: function(state, updateUI) { + //setSortState: function(state, doSort) { + setSortState: function(state, skipClear, initial) { + var me = this, + colSortClsPrefix = Ext.baseCSSPrefix + 'column-header-sort-', + ascCls = colSortClsPrefix + 'ASC', + descCls = colSortClsPrefix + 'DESC', + nullCls = colSortClsPrefix + 'null', + ownerHeaderCt = me.getOwnerHeaderCt(), + oldSortState = me.sortState; + + if (oldSortState !== state && me.getSortParam()) { + me.addCls(colSortClsPrefix + state); + // don't trigger a sort on the first time, we just want to update the UI + if (state && !initial) { + me.doSort(state); } + switch (state) { + case 'DESC': + me.removeCls([ascCls, nullCls]); + break; + case 'ASC': + me.removeCls([descCls, nullCls]); + break; + case null: + me.removeCls([ascCls, descCls]); + break; + } + if (ownerHeaderCt && !me.triStateSort && !skipClear) { + ownerHeaderCt.clearOtherSortStates(me); + } + me.sortState = state; + ownerHeaderCt.fireEvent('sortchange', ownerHeaderCt, me, state); } + }, + + hide: function() { + var me = this, + items, + len, i, + lb, + newWidth = 0, + ownerHeaderCt = me.getOwnerHeaderCt(); - availableWidth = size.width - me.scrollOffset; - me.innerCt.setWidth(availableWidth); + // Hiding means setting to zero width, so cache the width + me.oldWidth = me.getWidth(); - // some columns can be percentages while others are fixed - // so we need to make 2 passes - for (i = 0; i < len; i++) { - item = items[i]; - itemParallelMargins = parallelMargins[i] = item.getEl().getMargin('lr'); - if (!item.columnWidth) { - availableWidth -= (item.getWidth() + itemParallelMargins); + // Hiding a group header hides itself, and then informs the HeaderContainer about its sub headers (Suppressing header layout) + if (me.isGroupHeader) { + items = me.items.items; + me.callParent(arguments); + ownerHeaderCt.onHeaderHide(me); + for (i = 0, len = items.length; i < len; i++) { + items[i].hidden = true; + ownerHeaderCt.onHeaderHide(items[i], true); } + return; } - availableWidth = availableWidth < 0 ? 0 : availableWidth; - for (i = 0; i < len; i++) { - item = items[i]; - if (item.columnWidth) { - columnWidth = Math.floor(item.columnWidth * availableWidth) - parallelMargins[i]; - if (item.getWidth() != columnWidth) { - me.setItemSize(item, columnWidth, item.height); + // TODO: Work with Jamie to produce a scheme where we can show/hide/resize without triggering a layout cascade + lb = me.ownerCt.componentLayout.layoutBusy; + me.ownerCt.componentLayout.layoutBusy = true; + me.callParent(arguments); + me.ownerCt.componentLayout.layoutBusy = lb; + + // Notify owning HeaderContainer + ownerHeaderCt.onHeaderHide(me); + + if (me.ownerCt.isGroupHeader) { + // If we've just hidden the last header in a group, then hide the group + items = me.ownerCt.query('>:not([hidden])'); + if (!items.length) { + me.ownerCt.hide(); + } + // Size the group down to accommodate fewer sub headers + else { + for (i = 0, len = items.length; i < len; i++) { + newWidth += items[i].getWidth(); } + me.ownerCt.minWidth = newWidth; + me.ownerCt.setWidth(newWidth); } } + }, - // After the first pass on an autoScroll layout, restore the overflow settings if it had been changed (only changed for non-IE6) - if (!me.adjustmentPass && me.autoScroll) { + show: function() { + var me = this, + ownerCt = me.getOwnerHeaderCt(), + lb, + items, + len, i, + newWidth = 0; + + // TODO: Work with Jamie to produce a scheme where we can show/hide/resize without triggering a layout cascade + lb = me.ownerCt.componentLayout.layoutBusy; + me.ownerCt.componentLayout.layoutBusy = true; + me.callParent(arguments); + me.ownerCt.componentLayout.layoutBusy = lb; - // If there's a vertical overflow, relay with scrollbars - target.setStyle('overflow', 'auto'); - me.adjustmentPass = (target.dom.scrollHeight > size.height); - if (Ext.isIE6 || Ext.isIE7 || Ext.isIEQuirks) { - me.adjustedWidth = size.width - Ext.getScrollBarWidth(); - } else { - target.setStyle('overflow', 'auto'); + // If a sub header, ensure that the group header is visible + if (me.isSubHeader) { + if (!me.ownerCt.isVisible()) { + me.ownerCt.show(); } + } - // If the layout caused height overflow, recurse back and recalculate (with overflow setting restored on non-IE6) - if (me.adjustmentPass) { - me.onLayout(); + // If we've just shown a group with all its sub headers hidden, then show all its sub headers + if (me.isGroupHeader && !me.query(':not([hidden])').length) { + items = me.query('>*'); + for (i = 0, len = items.length; i < len; i++) { + items[i].show(); } } - delete me.adjustmentPass; + + // Resize the owning group to accommodate + if (me.ownerCt.isGroupHeader) { + items = me.ownerCt.query('>:not([hidden])'); + for (i = 0, len = items.length; i < len; i++) { + newWidth += items[i].getWidth(); + } + me.ownerCt.minWidth = newWidth; + me.ownerCt.setWidth(newWidth); + } + + // Notify owning HeaderContainer + if (ownerCt) { + ownerCt.onHeaderShow(me); + } + }, + + getDesiredWidth: function() { + var me = this; + if (me.rendered && me.componentLayout && me.componentLayout.lastComponentSize) { + // headers always have either a width or a flex + // because HeaderContainer sets a defaults width + // therefore we can ignore the natural width + // we use the componentLayout's tracked width so that + // we can calculate the desired width when rendered + // but not visible because its being obscured by a layout + return me.componentLayout.lastComponentSize.width; + // Flexed but yet to be rendered this could be the case + // where a HeaderContainer and Headers are simply used as data + // structures and not rendered. + } + else if (me.flex) { + // this is going to be wrong, the defaultWidth + return me.width; + } + else { + return me.width; + } + }, + + getCellSelector: function() { + return '.' + Ext.baseCSSPrefix + 'grid-cell-' + this.getItemId(); + }, + + getCellInnerSelector: function() { + return this.getCellSelector() + ' .' + Ext.baseCSSPrefix + 'grid-cell-inner'; + }, + + isOnLeftEdge: function(e) { + return (e.getXY()[0] - this.el.getLeft() <= this.handleWidth); + }, + + isOnRightEdge: function(e) { + return (this.el.getRight() - e.getXY()[0] <= this.handleWidth); } + + /** + * Retrieves the editing field for editing associated with this header. Returns false if there + * is no field associated with the Header the method will return false. If the + * field has not been instantiated it will be created. Note: These methods only has an implementation + * if a Editing plugin has been enabled on the grid. + * @param record The {@link Ext.data.Model Model} instance being edited. + * @param {Mixed} defaultField An object representing a default field to be created + * @returns {Ext.form.field.Field} field + * @method getEditor + */ + // intentionally omit getEditor and setEditor definitions bc we applyIf into columns + // when the editing plugin is injected + + + /** + * Sets the form field to be used for editing. Note: This method only has an implementation + * if an Editing plugin has been enabled on the grid. + * @param {Mixed} field An object representing a field to be created. If no xtype is specified a 'textfield' is assumed. + * @method setEditor + */ });