4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-grid-header-Container'>/**
19 </span> * @class Ext.grid.header.Container
20 * @extends Ext.container.Container
23 * Container which holds headers and is docked at the top or bottom of a TablePanel.
24 * The HeaderContainer drives resizing/moving/hiding of columns within the TableView.
25 * As headers are hidden, moved or resized the headercontainer is responsible for
26 * triggering changes within the view.
28 * @xtype headercontainer
30 Ext.define('Ext.grid.header.Container', {
31 extend: 'Ext.container.Container',
33 'Ext.grid.ColumnLayout',
34 'Ext.grid.column.Column',
38 'Ext.grid.plugin.HeaderResizer',
39 'Ext.grid.plugin.HeaderReorderer'
43 alias: 'widget.headercontainer',
45 baseCls: Ext.baseCSSPrefix + 'grid-header-ct',
48 <span id='Ext-grid-header-Container-cfg-weight'> /**
49 </span> * @cfg {Number} weight
50 * HeaderContainer overrides the default weight of 0 for all docked items to 100.
51 * This is so that it has more priority over things like toolbars.
54 defaultType: 'gridcolumn',
55 <span id='Ext-grid-header-Container-cfg-defaultWidth'> /**
56 </span> * @cfg {Number} defaultWidth
57 * Width of the header if no width or flex is specified. Defaults to 100.
62 sortAscText: 'Sort Ascending',
63 sortDescText: 'Sort Descending',
64 sortClearText: 'Clear Sort',
65 columnsText: 'Columns',
67 lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last',
68 firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first',
69 headerOpenCls: Ext.baseCSSPrefix + 'column-header-open',
71 // private; will probably be removed by 4.0
78 <span id='Ext-grid-header-Container-property-isGroupHeader'> /**
79 </span> * <code>true</code> if this HeaderContainer is in fact a group header which contains sub headers.
81 * @property isGroupHeader
84 <span id='Ext-grid-header-Container-cfg-sortable'> /**
85 </span> * @cfg {Boolean} sortable
86 * Provides the default sortable state for all Headers within this HeaderContainer.
87 * Also turns on or off the menus in the HeaderContainer. Note that the menu is
88 * shared across every header and therefore turning it off will remove the menu
89 * items for every header.
93 initComponent: function() {
97 me.plugins = me.plugins || [];
99 // TODO: Pass in configurations to turn on/off dynamic
100 // resizing and disable resizing all together
102 // Only set up a Resizer and Reorderer for the topmost HeaderContainer.
103 // Nested Group Headers are themselves HeaderContainers
105 me.resizer = Ext.create('Ext.grid.plugin.HeaderResizer');
106 me.reorderer = Ext.create('Ext.grid.plugin.HeaderReorderer');
107 if (!me.enableColumnResize) {
108 me.resizer.disable();
110 if (!me.enableColumnMove) {
111 me.reorderer.disable();
113 me.plugins.push(me.reorderer, me.resizer);
116 // Base headers do not need a box layout
117 if (me.isHeader && !me.items) {
120 // HeaderContainer and Group header needs a gridcolumn layout.
124 availableSpaceOffset: me.availableSpaceOffset,
129 me.defaults = me.defaults || {};
130 Ext.applyIf(me.defaults, {
131 width: me.defaultWidth,
132 triStateSort: me.triStateSort,
133 sortable: me.sortable
137 <span id='Ext-grid-header-Container-event-columnresize'> /**
138 </span> * @event columnresize
139 * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
140 * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
141 * @param {Number} width
145 <span id='Ext-grid-header-Container-event-headerclick'> /**
146 </span> * @event headerclick
147 * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
148 * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
149 * @param {Ext.EventObject} e
150 * @param {HTMLElement} t
154 <span id='Ext-grid-header-Container-event-headertriggerclick'> /**
155 </span> * @event headertriggerclick
156 * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
157 * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
158 * @param {Ext.EventObject} e
159 * @param {HTMLElement} t
161 'headertriggerclick',
163 <span id='Ext-grid-header-Container-event-columnmove'> /**
164 </span> * @event columnmove
165 * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
166 * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
167 * @param {Number} fromIdx
168 * @param {Number} toIdx
171 <span id='Ext-grid-header-Container-event-columnhide'> /**
172 </span> * @event columnhide
173 * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
174 * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
177 <span id='Ext-grid-header-Container-event-columnshow'> /**
178 </span> * @event columnshow
179 * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
180 * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
183 <span id='Ext-grid-header-Container-event-sortchange'> /**
184 </span> * @event sortchange
185 * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
186 * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
187 * @param {String} direction
190 <span id='Ext-grid-header-Container-event-menucreate'> /**
191 </span> * @event menucreate
192 * Fired immediately after the column header menu is created.
193 * @param {Ext.grid.header.Container} ct This instance
194 * @param {Ext.menu.Menu} menu The Menu that was created
200 onDestroy: function() {
201 Ext.destroy(this.resizer, this.reorderer);
205 // Invalidate column cache on add
206 // We cannot refresh the View on every add because this method is called
207 // when the HeaderDropZone moves Headers around, that will also refresh the view
211 c.headerId = 'h' + (++me.headerCounter);
213 me.callParent(arguments);
217 // Invalidate column cache on remove
218 // We cannot refresh the View on every remove because this method is called
219 // when the HeaderDropZone moves Headers around, that will also refresh the view
220 onRemove: function(c) {
222 me.callParent(arguments);
226 afterRender: function() {
228 var store = this.up('[store]').store,
229 sorters = store.sorters,
230 first = sorters.first(),
234 hd = this.down('gridcolumn[dataIndex=' + first.property +']');
236 hd.setSortState(first.direction, false, true);
241 afterLayout: function() {
242 if (!this.isHeader) {
244 topHeaders = me.query('>gridcolumn:not([hidden])'),
247 me.callParent(arguments);
249 if (topHeaders.length) {
250 topHeaders[0].el.radioCls(me.firstHeaderCls);
251 topHeaders[topHeaders.length - 1].el.radioCls(me.lastHeaderCls);
256 onHeaderShow: function(header) {
257 // Pass up to the GridSection
259 gridSection = me.ownerCt,
261 topItems, topItemsVisible,
268 colCheckItem = menu.down('menucheckitem[headerId=' + header.id + ']');
270 colCheckItem.setChecked(true, true);
273 // There's more than one header visible, and we've disabled some checked items... re-enable them
274 topItems = menu.query('#columnItem>menucheckitem[checked]');
275 topItemsVisible = topItems.length;
276 if ((me.getVisibleGridColumns().length > 1) && me.disabledMenuItems && me.disabledMenuItems.length) {
277 if (topItemsVisible == 1) {
278 Ext.Array.remove(me.disabledMenuItems, topItems[0]);
280 for (i = 0, len = me.disabledMenuItems.length; i < len; i++) {
281 itemToEnable = me.disabledMenuItems[i];
282 if (!itemToEnable.isDestroyed) {
283 itemToEnable[itemToEnable.menu ? 'enableCheckChange' : 'enable']();
286 if (topItemsVisible == 1) {
287 me.disabledMenuItems = topItems;
289 me.disabledMenuItems = [];
294 // Only update the grid UI when we are notified about base level Header shows;
295 // Group header shows just cause a layout of the HeaderContainer
296 if (!header.isGroupHeader) {
298 me.view.onHeaderShow(me, header, true);
301 gridSection.onHeaderShow(me, header);
304 me.fireEvent('columnshow', me, header);
306 // The header's own hide suppresses cascading layouts, so lay the headers out now
310 onHeaderHide: function(header, suppressLayout) {
311 // Pass up to the GridSection
313 gridSection = me.ownerCt,
319 // If the header was hidden programmatically, sync the Menu state
320 colCheckItem = menu.down('menucheckitem[headerId=' + header.id + ']');
322 colCheckItem.setChecked(false, true);
324 me.setDisabledItems();
327 // Only update the UI when we are notified about base level Header hides;
328 if (!header.isGroupHeader) {
330 me.view.onHeaderHide(me, header, true);
333 gridSection.onHeaderHide(me, header);
336 // The header's own hide suppresses cascading layouts, so lay the headers out now
337 if (!suppressLayout) {
341 me.fireEvent('columnhide', me, header);
344 setDisabledItems: function(){
352 // Find what to disable. If only one top level item remaining checked, we have to disable stuff.
353 itemsToDisable = menu.query('#columnItem>menucheckitem[checked]');
354 if ((itemsToDisable.length === 1)) {
355 if (!me.disabledMenuItems) {
356 me.disabledMenuItems = [];
359 // If down to only one column visible, also disable any descendant checkitems
360 if ((me.getVisibleGridColumns().length === 1) && itemsToDisable[0].menu) {
361 itemsToDisable = itemsToDisable.concat(itemsToDisable[0].menu.query('menucheckitem[checked]'));
364 len = itemsToDisable.length;
365 // Disable any further unchecking at any level.
366 for (i = 0; i < len; i++) {
367 itemToDisable = itemsToDisable[i];
368 if (!Ext.Array.contains(me.disabledMenuItems, itemToDisable)) {
369 itemToDisable[itemToDisable.menu ? 'disableCheckChange' : 'disable']();
370 me.disabledMenuItems.push(itemToDisable);
376 <span id='Ext-grid-header-Container-method-tempLock'> /**
377 </span> * Temporarily lock the headerCt. This makes it so that clicking on headers
378 * don't trigger actions like sorting or opening of the header menu. This is
379 * done because extraneous events may be fired on the headers after interacting
380 * with a drag drop operation.
383 tempLock: function() {
385 Ext.Function.defer(function() {
390 onHeaderResize: function(header, w, suppressFocus) {
392 if (this.view && this.view.rendered) {
393 this.view.onHeaderResize(header, w, suppressFocus);
395 this.fireEvent('columnresize', this, header, w);
398 onHeaderClick: function(header, e, t) {
399 this.fireEvent("headerclick", this, header, e, t);
402 onHeaderTriggerClick: function(header, e, t) {
403 // generate and cache menu, provide ability to cancel/etc
404 if (this.fireEvent("headertriggerclick", this, header, e, t) !== false) {
405 this.showMenuBy(t, header);
409 showMenuBy: function(t, header) {
410 var menu = this.getMenu(),
411 ascItem = menu.down('#ascItem'),
412 descItem = menu.down('#descItem'),
415 menu.activeHeader = menu.ownerCt = header;
416 menu.setFloatParent(header);
417 // TODO: remove coupling to Header's titleContainer el
418 header.titleContainer.addCls(this.headerOpenCls);
420 // enable or disable asc & desc menu items based on header being sortable
421 sortableMth = header.sortable ? 'enable' : 'disable';
423 ascItem[sortableMth]();
426 descItem[sortableMth]();
431 // remove the trigger open class when the menu is hidden
432 onMenuDeactivate: function() {
433 var menu = this.getMenu();
434 // TODO: remove coupling to Header's titleContainer el
435 menu.activeHeader.titleContainer.removeCls(this.headerOpenCls);
438 moveHeader: function(fromIdx, toIdx) {
440 // An automatically expiring lock
442 this.onHeaderMoved(this.move(fromIdx, toIdx), fromIdx, toIdx);
445 purgeCache: function() {
447 // Delete column cache - column order has changed.
448 delete me.gridDataColumns;
450 // Menu changes when columns are moved. It will be recreated.
457 onHeaderMoved: function(header, fromIdx, toIdx) {
459 gridSection = me.ownerCt;
462 gridSection.onHeaderMove(me, header, fromIdx, toIdx);
464 me.fireEvent("columnmove", me, header, fromIdx, toIdx);
467 <span id='Ext-grid-header-Container-method-getMenu'> /**
468 </span> * Gets the menu (and will create it if it doesn't already exist)
471 getMenu: function() {
475 me.menu = Ext.create('Ext.menu.Menu', {
476 items: me.getMenuItems(),
478 deactivate: me.onMenuDeactivate,
482 me.setDisabledItems();
483 me.fireEvent('menucreate', me, me.menu);
488 <span id='Ext-grid-header-Container-method-getMenuItems'> /**
489 </span> * Returns an array of menu items to be placed into the shared menu
490 * across all headers in this header container.
491 * @returns {Array} menuItems
493 getMenuItems: function() {
496 itemId: 'columnItem',
497 text: me.columnsText,
498 cls: Ext.baseCSSPrefix + 'cols-icon',
499 menu: me.getColumnMenu(me)
505 text: me.sortAscText,
506 cls: 'xg-hmenu-sort-asc',
507 handler: me.onSortAscClick,
511 text: me.sortDescText,
512 cls: 'xg-hmenu-sort-desc',
513 handler: me.onSortDescClick,
520 // sort asc when clicking on item in menu
521 onSortAscClick: function() {
522 var menu = this.getMenu(),
523 activeHeader = menu.activeHeader;
525 activeHeader.setSortState('ASC');
528 // sort desc when clicking on item in menu
529 onSortDescClick: function() {
530 var menu = this.getMenu(),
531 activeHeader = menu.activeHeader;
533 activeHeader.setSortState('DESC');
536 <span id='Ext-grid-header-Container-method-getColumnMenu'> /**
537 </span> * Returns an array of menu CheckItems corresponding to all immediate children of the passed Container which have been configured as hideable.
539 getColumnMenu: function(headerContainer) {
543 items = headerContainer.query('>gridcolumn[hideable]'),
544 itemsLn = items.length,
547 for (; i < itemsLn; i++) {
549 menuItem = Ext.create('Ext.menu.CheckItem', {
551 checked: !item.hidden,
554 menu: item.isGroupHeader ? this.getColumnMenu(item) : undefined,
555 checkHandler: this.onColumnCheckChange,
559 menuItem.disabled = true;
561 menuItems.push(menuItem);
563 // If the header is ever destroyed - for instance by dragging out the last remaining sub header,
564 // then the associated menu item must also be destroyed.
566 destroy: Ext.Function.bind(menuItem.destroy, menuItem)
572 onColumnCheckChange: function(checkItem, checked) {
573 var header = Ext.getCmp(checkItem.headerId);
574 header[checked ? 'show' : 'hide']();
577 <span id='Ext-grid-header-Container-method-getColumnsForTpl'> /**
578 </span> * Get the columns used for generating a template via TableChunker.
579 * Returns an array of all columns and their
584 * - columnId - used to create an identifying CSS class
585 * - cls The tdCls configuration from the Column object
588 getColumnsForTpl: function(flushCache) {
590 headers = this.getGridColumns(flushCache),
591 headersLn = headers.length,
595 for (; i < headersLn; i++) {
598 dataIndex: header.dataIndex,
600 width: header.hidden ? 0 : header.getDesiredWidth(),
603 columnId: header.getItemId()
609 <span id='Ext-grid-header-Container-method-getColumnCount'> /**
610 </span> * Returns the number of <b>grid columns</b> descended from this HeaderContainer.
611 * Group Columns are HeaderContainers. All grid columns are returned, including hidden ones.
613 getColumnCount: function() {
614 return this.getGridColumns().length;
617 <span id='Ext-grid-header-Container-method-getFullWidth'> /**
618 </span> * Gets the full width of all columns that are visible.
620 getFullWidth: function(flushCache) {
622 headers = this.getVisibleGridColumns(flushCache),
623 headersLn = headers.length,
626 for (; i < headersLn; i++) {
627 if (!isNaN(headers[i].width)) {
628 // use headers getDesiredWidth if its there
629 if (headers[i].getDesiredWidth) {
630 fullWidth += headers[i].getDesiredWidth();
631 // if injected a diff cmp use getWidth
633 fullWidth += headers[i].getWidth();
640 // invoked internally by a header when not using triStateSorting
641 clearOtherSortStates: function(activeHeader) {
642 var headers = this.getGridColumns(),
643 headersLn = headers.length,
647 for (; i < headersLn; i++) {
648 if (headers[i] !== activeHeader) {
649 oldSortState = headers[i].sortState;
650 // unset the sortstate and dont recurse
651 headers[i].setSortState(null, true);
652 //if (!silent && oldSortState !== null) {
653 // this.fireEvent('sortchange', this, headers[i], null);
659 <span id='Ext-grid-header-Container-method-getVisibleGridColumns'> /**
660 </span> * Returns an array of the <b>visible<b> columns in the grid. This goes down to the lowest column header
661 * level, and does not return <i>grouped</i> headers which contain sub headers.
662 * @param {Boolean} refreshCache If omitted, the cached set of columns will be returned. Pass true to refresh the cache.
665 getVisibleGridColumns: function(refreshCache) {
666 return Ext.ComponentQuery.query(':not([hidden])', this.getGridColumns(refreshCache));
669 <span id='Ext-grid-header-Container-method-getGridColumns'> /**
670 </span> * Returns an array of all columns which map to Store fields. This goes down to the lowest column header
671 * level, and does not return <i>grouped</i> headers which contain sub headers.
672 * @param {Boolean} refreshCache If omitted, the cached set of columns will be returned. Pass true to refresh the cache.
675 getGridColumns: function(refreshCache) {
677 result = refreshCache ? null : me.gridDataColumns;
679 // Not already got the column cache, so collect the base columns
681 me.gridDataColumns = result = [];
682 me.cascade(function(c) {
683 if ((c !== me) && !c.isGroupHeader) {
692 <span id='Ext-grid-header-Container-method-getHeaderIndex'> /**
693 </span> * Get the index of a leaf level header regardless of what the nesting
696 getHeaderIndex: function(header) {
697 var columns = this.getGridColumns();
698 return Ext.Array.indexOf(columns, header);
701 <span id='Ext-grid-header-Container-method-getHeaderAtIndex'> /**
702 </span> * Get a leaf level header by index regardless of what the nesting
705 getHeaderAtIndex: function(index) {
706 var columns = this.getGridColumns();
707 return columns[index];
710 <span id='Ext-grid-header-Container-method-prepareData'> /**
711 </span> * Maps the record data to base it on the header id's.
712 * This correlates to the markup/template generated by
715 prepareData: function(data, rowIdx, record, view, panel) {
717 headers = this.gridDataColumns || this.getGridColumns(),
718 headersLn = headers.length,
727 for (; colIdx < headersLn; colIdx++) {
732 header = headers[colIdx];
733 headerId = header.id;
734 renderer = header.renderer;
735 value = data[header.dataIndex];
737 // When specifying a renderer as a string, it always resolves
738 // to Ext.util.Format
739 if (typeof renderer === "string") {
740 header.renderer = renderer = Ext.util.Format[renderer];
743 if (typeof renderer === "function") {
744 value = renderer.call(
745 header.scope || this.ownerCt,
747 // metadata per cell passing an obj by reference so that
748 // it can be manipulated inside the renderer
760 // This warning attribute is used by the compat layer
761 obj.cssWarning = true;
762 metaData.tdCls = metaData.css;
767 obj[headerId+'-modified'] = record.modified[header.dataIndex] ? Ext.baseCSSPrefix + 'grid-dirty-cell' : Ext.baseCSSPrefix + 'grid-clean-cell';
768 obj[headerId+'-tdCls'] = metaData.tdCls;
769 obj[headerId+'-tdAttr'] = metaData.tdAttr;
770 obj[headerId+'-style'] = metaData.style;
771 if (value === undefined || value === null || value === '') {
772 value = '&#160;';
774 obj[headerId] = value;
779 expandToFit: function(header) {
781 this.view.expandToFit(header);