Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / tab / Tab.js
1 /**
2  * @author Ed Spencer
3  * @class Ext.tab.Tab
4  * @extends Ext.button.Button
5  * 
6  * <p>Represents a single Tab in a {@link Ext.tab.Panel TabPanel}. A Tab is simply a slightly customized {@link Ext.button.Button Button}, 
7  * styled to look like a tab. Tabs are optionally closable, and can also be disabled. 99% of the time you will not
8  * need to create Tabs manually as the framework does so automatically when you use a {@link Ext.tab.Panel TabPanel}</p>
9  *
10  * @xtype tab
11  */
12 Ext.define('Ext.tab.Tab', {
13     extend: 'Ext.button.Button',
14     alias: 'widget.tab',
15     
16     requires: [
17         'Ext.layout.component.Tab',
18         'Ext.util.KeyNav'
19     ],
20
21     componentLayout: 'tab',
22
23     isTab: true,
24
25     baseCls: Ext.baseCSSPrefix + 'tab',
26
27     /**
28      * @cfg {String} activeCls
29      * The CSS class to be applied to a Tab when it is active. Defaults to 'x-tab-active'.
30      * Providing your own CSS for this class enables you to customize the active state.
31      */
32     activeCls: 'active',
33     
34     /**
35      * @cfg {String} disabledCls
36      * The CSS class to be applied to a Tab when it is disabled. Defaults to 'x-tab-disabled'.
37      */
38
39     /**
40      * @cfg {String} closableCls
41      * The CSS class which is added to the tab when it is closable
42      */
43     closableCls: 'closable',
44
45     /**
46      * @cfg {Boolean} closable True to make the Tab start closable (the close icon will be visible). Defaults to true
47      */
48     closable: true,
49
50     /**
51      * @cfg {String} closeText 
52      * The accessible text label for the close button link; only used when {@link #closable} = true.
53      * Defaults to 'Close Tab'.
54      */
55     closeText: 'Close Tab',
56
57     /**
58      * @property Boolean
59      * Read-only property indicating that this tab is currently active. This is NOT a public configuration.
60      */
61     active: false,
62
63     /**
64      * @property closable
65      * @type Boolean
66      * True if the tab is currently closable
67      */
68
69     scale: false,
70
71     position: 'top',
72     
73     initComponent: function() {
74         var me = this;
75
76         me.addEvents(
77             /**
78              * @event activate
79              * @param {Ext.tab.Tab} this
80              */
81             'activate',
82
83             /**
84              * @event deactivate
85              * @param {Ext.tab.Tab} this
86              */
87             'deactivate',
88
89             /**
90              * @event beforeclose
91              * Fires if the user clicks on the Tab's close button, but before the {@link #close} event is fired. Return
92              * false from any listener to stop the close event being fired
93              * @param {Ext.tab.Tab} tab The Tab object
94              */
95             'beforeclose',
96
97             /**
98              * @event beforeclose
99              * Fires to indicate that the tab is to be closed, usually because the user has clicked the close button.
100              * @param {Ext.tab.Tab} tab The Tab object
101              */
102             'close'
103         );
104         
105         me.callParent(arguments);
106
107         if (me.card) {
108             me.setCard(me.card);
109         }
110     },
111
112     /**
113      * @ignore
114      */
115     onRender: function() {
116         var me = this;
117         
118         me.addClsWithUI(me.position);
119         
120         // Set all the state classNames, as they need to include the UI
121         // me.disabledCls = me.getClsWithUIs('disabled');
122
123         me.syncClosableUI();
124
125         me.callParent(arguments);
126         
127         if (me.active) {
128             me.activate(true);
129         }
130
131         me.syncClosableElements();
132         
133         me.keyNav = Ext.create('Ext.util.KeyNav', me.el, {
134             enter: me.onEnterKey,
135             del: me.onDeleteKey,
136             scope: me
137         });
138     },
139     
140     // inherit docs
141     enable : function(silent) {
142         var me = this;
143
144         me.callParent(arguments);
145         
146         me.removeClsWithUI(me.position + '-disabled');
147
148         return me;
149     },
150
151     // inherit docs
152     disable : function(silent) {
153         var me = this;
154         
155         me.callParent(arguments);
156         
157         me.addClsWithUI(me.position + '-disabled');
158
159         return me;
160     },
161     
162     /**
163      * @ignore
164      */
165     onDestroy: function() {
166         var me = this;
167
168         if (me.closeEl) {
169             me.closeEl.un('click', Ext.EventManager.preventDefault);
170             me.closeEl = null;
171         }
172
173         Ext.destroy(me.keyNav);
174         delete me.keyNav;
175
176         me.callParent(arguments);
177     },
178
179     /**
180      * Sets the tab as either closable or not
181      * @param {Boolean} closable Pass false to make the tab not closable. Otherwise the tab will be made closable (eg a
182      * close button will appear on the tab)
183      */
184     setClosable: function(closable) {
185         var me = this;
186
187         // Closable must be true if no args
188         closable = (!arguments.length || !!closable);
189
190         if (me.closable != closable) {
191             me.closable = closable;
192
193             // set property on the user-facing item ('card'):
194             if (me.card) {
195                 me.card.closable = closable;
196             }
197
198             me.syncClosableUI();
199
200             if (me.rendered) {
201                 me.syncClosableElements();
202
203                 // Tab will change width to accommodate close icon
204                 me.doComponentLayout();
205                 if (me.ownerCt) {
206                     me.ownerCt.doLayout();
207                 }
208             }
209         }
210     },
211
212     /**
213      * This method ensures that the closeBtn element exists or not based on 'closable'.
214      * @private
215      */
216     syncClosableElements: function () {
217         var me = this;
218
219         if (me.closable) {
220             if (!me.closeEl) {
221                 me.closeEl = me.el.createChild({
222                     tag: 'a',
223                     cls: me.baseCls + '-close-btn',
224                     href: '#',
225                     html: me.closeText,
226                     title: me.closeText
227                 }).on('click', Ext.EventManager.preventDefault);  // mon ???
228             }
229         } else {
230             var closeEl = me.closeEl;
231             if (closeEl) {
232                 closeEl.un('click', Ext.EventManager.preventDefault);
233                 closeEl.remove();
234                 me.closeEl = null;
235             }
236         }
237     },
238
239     /**
240      * This method ensures that the UI classes are added or removed based on 'closable'.
241      * @private
242      */
243     syncClosableUI: function () {
244         var me = this, classes = [me.closableCls, me.closableCls + '-' + me.position];
245
246         if (me.closable) {
247             me.addClsWithUI(classes);
248         } else {
249             me.removeClsWithUI(classes);
250         }
251     },
252
253     /**
254      * Sets this tab's attached card. Usually this is handled automatically by the {@link Ext.tab.Panel} that this Tab
255      * belongs to and would not need to be done by the developer
256      * @param {Ext.Component} card The card to set
257      */
258     setCard: function(card) {
259         var me = this;
260
261         me.card = card;
262         me.setText(me.title || card.title);
263         me.setIconCls(me.iconCls || card.iconCls);
264     },
265
266     /**
267      * @private
268      * Listener attached to click events on the Tab's close button
269      */
270     onCloseClick: function() {
271         var me = this;
272
273         if (me.fireEvent('beforeclose', me) !== false) {
274             if (me.tabBar) {
275                 me.tabBar.closeTab(me);
276             }
277
278             me.fireEvent('close', me);
279         }
280     },
281     
282     /**
283      * @private
284      */
285     onEnterKey: function(e) {
286         var me = this;
287         
288         if (me.tabBar) {
289             me.tabBar.onClick(e, me.el);
290         }
291     },
292     
293    /**
294      * @private
295      */
296     onDeleteKey: function(e) {
297         var me = this;
298         
299         if (me.closable) {
300             me.onCloseClick();
301         }
302     },
303     
304     // @private
305     activate : function(supressEvent) {
306         var me = this;
307         
308         me.active = true;
309         me.addClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);
310
311         if (supressEvent !== true) {
312             me.fireEvent('activate', me);
313         }
314     },
315
316     // @private
317     deactivate : function(supressEvent) {
318         var me = this;
319         
320         me.active = false;
321         me.removeClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);
322         
323         if (supressEvent !== true) {
324             me.fireEvent('deactivate', me);
325         }
326     }
327 });