Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / panel / Header.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
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.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.panel.Header
17  * @extends Ext.container.Container
18  * Simple header class which is used for on {@link Ext.panel.Panel} and {@link Ext.window.Window}
19  */
20 Ext.define('Ext.panel.Header', {
21     extend: 'Ext.container.Container',
22     uses: ['Ext.panel.Tool', 'Ext.draw.Component', 'Ext.util.CSS'],
23     alias: 'widget.header',
24
25     isHeader       : true,
26     defaultType    : 'tool',
27     indicateDrag   : false,
28     weight         : -1,
29
30     renderTpl: [
31         '<div id="{id}-body" class="{baseCls}-body<tpl if="bodyCls"> {bodyCls}</tpl>',
32         '<tpl if="uiCls">',
33             '<tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl>',
34         '</tpl>"',
35         '<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>></div>'],
36
37     /**
38      * @cfg {String} title
39      * The title text to display
40      */
41
42     /**
43      * @cfg {String} iconCls
44      * CSS class for icon in header. Used for displaying an icon to the left of a title.
45      */
46
47     initComponent: function() {
48         var me = this,
49             ruleStyle,
50             rule,
51             style,
52             titleTextEl,
53             ui;
54
55         me.indicateDragCls = me.baseCls + '-draggable';
56         me.title = me.title || '&#160;';
57         me.tools = me.tools || [];
58         me.items = me.items || [];
59         me.orientation = me.orientation || 'horizontal';
60         me.dock = (me.dock) ? me.dock : (me.orientation == 'horizontal') ? 'top' : 'left';
61
62         //add the dock as a ui
63         //this is so we support top/right/left/bottom headers
64         me.addClsWithUI(me.orientation);
65         me.addClsWithUI(me.dock);
66
67         me.addChildEls('body');
68
69         // Add Icon
70         if (!Ext.isEmpty(me.iconCls)) {
71             me.initIconCmp();
72             me.items.push(me.iconCmp);
73         }
74
75         // Add Title
76         if (me.orientation == 'vertical') {
77             // Hack for IE6/7's inability to display an inline-block
78             if (Ext.isIE6 || Ext.isIE7) {
79                 me.width = this.width || 24;
80             } else if (Ext.isIEQuirks) {
81                 me.width = this.width || 25;
82             }
83
84             me.layout = {
85                 type : 'vbox',
86                 align: 'center',
87                 clearInnerCtOnLayout: true,
88                 bindToOwnerCtContainer: false
89             };
90             me.textConfig = {
91                 cls: me.baseCls + '-text',
92                 type: 'text',
93                 text: me.title,
94                 rotate: {
95                     degrees: 90
96                 }
97             };
98             ui = me.ui;
99             if (Ext.isArray(ui)) {
100                 ui = ui[0];
101             }
102             ruleStyle = '.' + me.baseCls + '-text-' + ui;
103             if (Ext.scopeResetCSS) {
104                 ruleStyle = '.' + Ext.baseCSSPrefix + 'reset ' + ruleStyle;
105             }
106             rule = Ext.util.CSS.getRule(ruleStyle);
107             if (rule) {
108                 style = rule.style;
109             }
110             if (style) {
111                 Ext.apply(me.textConfig, {
112                     'font-family': style.fontFamily,
113                     'font-weight': style.fontWeight,
114                     'font-size': style.fontSize,
115                     fill: style.color
116                 });
117             }
118             me.titleCmp = Ext.create('Ext.draw.Component', {
119                 ariaRole  : 'heading',
120                 focusable: false,
121                 viewBox: false,
122                 flex : 1,
123                 autoSize: true,
124                 margins: '5 0 0 0',
125                 items: [ me.textConfig ],
126                 // this is a bit of a cheat: we are not selecting an element of titleCmp
127                 // but rather of titleCmp.items[0] (so we cannot use childEls)
128                 renderSelectors: {
129                     textEl: '.' + me.baseCls + '-text'
130                 }
131             });
132         } else {
133             me.layout = {
134                 type : 'hbox',
135                 align: 'middle',
136                 clearInnerCtOnLayout: true,
137                 bindToOwnerCtContainer: false
138             };
139             me.titleCmp = Ext.create('Ext.Component', {
140                 xtype     : 'component',
141                 ariaRole  : 'heading',
142                 focusable: false,
143                 flex : 1,
144                 cls: me.baseCls + '-text-container',
145                 renderTpl : [
146                     '<span id="{id}-textEl" class="{cls}-text {cls}-text-{ui}">{title}</span>'
147                 ],
148                 renderData: {
149                     title: me.title,
150                     cls  : me.baseCls,
151                     ui   : me.ui
152                 },
153                 childEls: ['textEl']
154             });
155         }
156         me.items.push(me.titleCmp);
157
158         // Add Tools
159         me.items = me.items.concat(me.tools);
160         this.callParent();
161     },
162
163     initIconCmp: function() {
164         this.iconCmp = Ext.create('Ext.Component', {
165             focusable: false,
166             renderTpl : [
167                 '<img id="{id}-iconEl" alt="" src="{blank}" class="{cls}-icon {iconCls}"/>'
168             ],
169             renderData: {
170                 blank  : Ext.BLANK_IMAGE_URL,
171                 cls    : this.baseCls,
172                 iconCls: this.iconCls,
173                 orientation: this.orientation
174             },
175             childEls: ['iconEl'],
176             iconCls: this.iconCls
177         });
178     },
179
180     afterRender: function() {
181         var me = this;
182
183         me.el.unselectable();
184         if (me.indicateDrag) {
185             me.el.addCls(me.indicateDragCls);
186         }
187         me.mon(me.el, {
188             click: me.onClick,
189             scope: me
190         });
191         me.callParent();
192     },
193
194     afterLayout: function() {
195         var me = this;
196         me.callParent(arguments);
197
198         // IE7 needs a forced repaint to make the top framing div expand to full width
199         if (Ext.isIE7) {
200             me.el.repaint();
201         }
202     },
203
204     // inherit docs
205     addUIClsToElement: function(cls, force) {
206         var me = this,
207             result = me.callParent(arguments),
208             classes = [me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls],
209             array, i;
210
211         if (!force && me.rendered) {
212             if (me.bodyCls) {
213                 me.body.addCls(me.bodyCls);
214             } else {
215                 me.body.addCls(classes);
216             }
217         } else {
218             if (me.bodyCls) {
219                 array = me.bodyCls.split(' ');
220
221                 for (i = 0; i < classes.length; i++) {
222                     if (!Ext.Array.contains(array, classes[i])) {
223                         array.push(classes[i]);
224                     }
225                 }
226
227                 me.bodyCls = array.join(' ');
228             } else {
229                 me.bodyCls = classes.join(' ');
230             }
231         }
232
233         return result;
234     },
235
236     // inherit docs
237     removeUIClsFromElement: function(cls, force) {
238         var me = this,
239             result = me.callParent(arguments),
240             classes = [me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls],
241             array, i;
242
243         if (!force && me.rendered) {
244             if (me.bodyCls) {
245                 me.body.removeCls(me.bodyCls);
246             } else {
247                 me.body.removeCls(classes);
248             }
249         } else {
250             if (me.bodyCls) {
251                 array = me.bodyCls.split(' ');
252
253                 for (i = 0; i < classes.length; i++) {
254                     Ext.Array.remove(array, classes[i]);
255                 }
256
257                 me.bodyCls = array.join(' ');
258             }
259         }
260
261        return result;
262     },
263
264     // inherit docs
265     addUIToElement: function(force) {
266         var me = this,
267             array, cls;
268
269         me.callParent(arguments);
270
271         cls = me.baseCls + '-body-' + me.ui;
272         if (!force && me.rendered) {
273             if (me.bodyCls) {
274                 me.body.addCls(me.bodyCls);
275             } else {
276                 me.body.addCls(cls);
277             }
278         } else {
279             if (me.bodyCls) {
280                 array = me.bodyCls.split(' ');
281
282                 if (!Ext.Array.contains(array, cls)) {
283                     array.push(cls);
284                 }
285
286                 me.bodyCls = array.join(' ');
287             } else {
288                 me.bodyCls = cls;
289             }
290         }
291
292         if (!force && me.titleCmp && me.titleCmp.rendered && me.titleCmp.textEl) {
293             me.titleCmp.textEl.addCls(me.baseCls + '-text-' + me.ui);
294         }
295     },
296
297     // inherit docs
298     removeUIFromElement: function() {
299         var me = this,
300             array, cls;
301
302         me.callParent(arguments);
303
304         cls = me.baseCls + '-body-' + me.ui;
305         if (me.rendered) {
306             if (me.bodyCls) {
307                 me.body.removeCls(me.bodyCls);
308             } else {
309                 me.body.removeCls(cls);
310             }
311         } else {
312             if (me.bodyCls) {
313                 array = me.bodyCls.split(' ');
314                 Ext.Array.remove(array, cls);
315                 me.bodyCls = array.join(' ');
316             } else {
317                 me.bodyCls = cls;
318             }
319         }
320
321         if (me.titleCmp && me.titleCmp.rendered && me.titleCmp.textEl) {
322             me.titleCmp.textEl.removeCls(me.baseCls + '-text-' + me.ui);
323         }
324     },
325
326     onClick: function(e) {
327         if (!e.getTarget(Ext.baseCSSPrefix + 'tool')) {
328             this.fireEvent('click', e);
329         }
330     },
331
332     getTargetEl: function() {
333         return this.body || this.frameBody || this.el;
334     },
335
336     /**
337      * Sets the title of the header.
338      * @param {String} title The title to be set
339      */
340     setTitle: function(title) {
341         var me = this;
342         if (me.rendered) {
343             if (me.titleCmp.rendered) {
344                 if (me.titleCmp.surface) {
345                     me.title = title || '';
346                     var sprite = me.titleCmp.surface.items.items[0],
347                         surface = me.titleCmp.surface;
348
349                     surface.remove(sprite);
350                     me.textConfig.type = 'text';
351                     me.textConfig.text = title;
352                     sprite = surface.add(me.textConfig);
353                     sprite.setAttributes({
354                         rotate: {
355                             degrees: 90
356                         }
357                     }, true);
358                     me.titleCmp.autoSizeSurface();
359                 } else {
360                     me.title = title || '&#160;';
361                     me.titleCmp.textEl.update(me.title);
362                 }
363             } else {
364                 me.titleCmp.on({
365                     render: function() {
366                         me.setTitle(title);
367                     },
368                     single: true
369                 });
370             }
371         } else {
372             me.on({
373                 render: function() {
374                     me.layout.layout();
375                     me.setTitle(title);
376                 },
377                 single: true
378             });
379         }
380     },
381
382     /**
383      * Sets the CSS class that provides the icon image for this header.  This method will replace any existing
384      * icon class if one has already been set.
385      * @param {String} cls The new CSS class name
386      */
387     setIconCls: function(cls) {
388         var me = this,
389             isEmpty = !cls || !cls.length,
390             iconCmp = me.iconCmp,
391             el;
392         
393         me.iconCls = cls;
394         if (!me.iconCmp && !isEmpty) {
395             me.initIconCmp();
396             me.insert(0, me.iconCmp);
397         } else if (iconCmp) {
398             if (isEmpty) {
399                 me.iconCmp.destroy();
400             } else {
401                 el = iconCmp.iconEl;
402                 el.removeCls(iconCmp.iconCls);
403                 el.addCls(cls);
404                 iconCmp.iconCls = cls;
405             }
406         }
407     },
408
409     /**
410      * Add a tool to the header
411      * @param {Object} tool
412      */
413     addTool: function(tool) {
414         this.tools.push(this.add(tool));
415     },
416
417     /**
418      * @private
419      * Set up the tools.&lt;tool type> link in the owning Panel.
420      * Bind the tool to its owning Panel.
421      * @param component
422      * @param index
423      */
424     onAdd: function(component, index) {
425         this.callParent([arguments]);
426         if (component instanceof Ext.panel.Tool) {
427             component.bindTo(this.ownerCt);
428             this.tools[component.type] = component;
429         }
430     }
431 });
432