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