X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/ee06f37b0f6f6d94cd05a6ffae556660f7c4a2bc..c930e9176a5a85509c5b0230e2bff5c22a591432:/docs/source/TabPanel.html?ds=sidebyside diff --git a/docs/source/TabPanel.html b/docs/source/TabPanel.html new file mode 100644 index 00000000..1ce6367c --- /dev/null +++ b/docs/source/TabPanel.html @@ -0,0 +1,1103 @@ + +
+/** + * @class Ext.TabPanel + *A basic tab container. TabPanels can be used exactly like a standard {@link Ext.Panel} + * for layout purposes, but also have special support for containing child Components + * ({@link Ext.Container#items items}) that are managed using a + * {@link Ext.layout.CardLayout CardLayout layout manager}, and displayed as separate tabs.
+ * + * Note: By default, a tab's close tool destroys the child tab Component + * and all its descendants. This makes the child tab Component, and all its descendants unusable. To enable + * re-use of a tab, configure the TabPanel with{@link #autoDestroy autoDestroy: false}
. + * + *TabPanel header/footer elements
+ *TabPanels use their {@link Ext.Panel#header header} or {@link Ext.Panel#footer footer} element + * (depending on the {@link #tabPosition} configuration) to accommodate the tab selector buttons. + * This means that a TabPanel will not display any configured title, and will not display any + * configured header {@link Ext.Panel#tools tools}.
+ *To display a header, embed the TabPanel in a {@link Ext.Panel Panel} which uses + * {@link Ext.Container#layout layout:'fit'}.
+ * + *Tab Events
+ *There is no actual tab class — each tab is simply a {@link Ext.BoxComponent Component} + * such as a {@link Ext.Panel Panel}. However, when rendered in a TabPanel, each child Component + * can fire additional events that only exist for tabs and are not available from other Components. + * These events are:
+ *+ *+ *
- {@link Ext.Panel#activate activate} : Fires when this Component becomes + * the active tab.
+ *- {@link Ext.Panel#deactivate deactivate} : Fires when the Component that + * was the active tab becomes deactivated.
+ *Creating TabPanels from Code
+ *TabPanels can be created and rendered completely in code, as in this example:
+ *+ *+var tabs = new Ext.TabPanel({ + renderTo: Ext.getBody(), + activeTab: 0, + items: [{ + title: 'Tab 1', + html: 'A simple tab' + },{ + title: 'Tab 2', + html: 'Another one' + }] +}); +
Creating TabPanels from Existing Markup
+ *TabPanels can also be rendered from pre-existing markup in a couple of ways.
+ *+ * + * @extends Ext.Panel + * @constructor + * @param {Object} config The configuration options + * @xtype tabpanel + */ +Ext.TabPanel = Ext.extend(Ext.Panel, { + /** + * @cfg {Boolean} layoutOnTabChange + * Set to true to force a layout of the active tab when the tab is changed. Defaults to false. + * See {@link Ext.layout.CardLayout}.+ * + *
- Pre-Structured Markup
+ *+ *+ * + *A container div with one or more nested tab divs with class 'x-tab' can be rendered entirely + * from existing markup (See the {@link #autoTabs} example).
+ *- Un-Structured Markup
+ *+ *+ * + *A TabPanel can also be rendered from markup that is not strictly structured by simply specifying by id + * which elements should be the container and the tabs. Using this method tab content can be pulled from different + * elements within the page by id regardless of page structure. For example:
+ *+ * Note that the tab divs in this example contain the class 'x-hide-display' so that they can be rendered + * deferred without displaying outside the tabs. You could alternately set {@link #deferredRender} = false + * to render all content tabs on page load. + *+var tabs = new Ext.TabPanel({ + renderTo: 'my-tabs', + activeTab: 0, + items:[ + {contentEl:'tab1', title:'Tab 1'}, + {contentEl:'tab2', title:'Tab 2'} + ] +}); + +// Note that the tabs do not have to be nested within the container (although they can be) +<div id="my-tabs"></div> +<div id="tab1" class="x-hide-display">A simple tab</div> +<div id="tab2" class="x-hide-display">Another one</div> +
{@link Ext.layout.CardLayout#layoutOnCardChange layoutOnCardChange}
. + */ + /** + * @cfg {String} tabCls This config option is used on child Components of ths TabPanel. A CSS + * class name applied to the tab strip item representing the child Component, allowing special + * styling to be applied. + */ + /** + * @cfg {Boolean} monitorResize True to automatically monitor window resize events and rerender the layout on + * browser resize (defaults to true). + */ + monitorResize : true, + /** + * @cfg {Boolean} deferredRender + *true by default to defer the rendering of child {@link Ext.Container#items items} + * to the browsers DOM until a tab is activated. false will render all contained + * {@link Ext.Container#items items} as soon as the {@link Ext.layout.CardLayout layout} + * is rendered. If there is a significant amount of content or a lot of heavy controls being + * rendered into panels that are not displayed by default, setting this to true might + * improve performance.
+ *The deferredRender property is internally passed to the layout manager for + * TabPanels ({@link Ext.layout.CardLayout}) as its {@link Ext.layout.CardLayout#deferredRender} + * configuration value.
+ *Note: leaving deferredRender as true means that the content + * within an unactivated tab will not be available. For example, this means that if the TabPanel + * is within a {@link Ext.form.FormPanel form}, then until a tab is activated, any Fields within + * unactivated tabs will not be rendered, and will therefore not be submitted and will not be + * available to either {@link Ext.form.BasicForm#getValues getValues} or + * {@link Ext.form.BasicForm#setValues setValues}.
+ */ + deferredRender : true, + /** + * @cfg {Number} tabWidth The initial width in pixels of each new tab (defaults to 120). + */ + tabWidth : 120, + /** + * @cfg {Number} minTabWidth The minimum width in pixels for each tab when {@link #resizeTabs} = true (defaults to 30). + */ + minTabWidth : 30, + /** + * @cfg {Boolean} resizeTabs True to automatically resize each tab so that the tabs will completely fill the + * tab strip (defaults to false). Setting this to true may cause specific widths that might be set per tab to + * be overridden in order to fit them all into view (although {@link #minTabWidth} will always be honored). + */ + resizeTabs : false, + /** + * @cfg {Boolean} enableTabScroll True to enable scrolling to tabs that may be invisible due to overflowing the + * overall TabPanel width. Only available with tabPosition:'top' (defaults to false). + */ + enableTabScroll : false, + /** + * @cfg {Number} scrollIncrement The number of pixels to scroll each time a tab scroll button is pressed + * (defaults to 100, or if {@link #resizeTabs} = true, the calculated tab width). Only + * applies when {@link #enableTabScroll} = true. + */ + scrollIncrement : 0, + /** + * @cfg {Number} scrollRepeatInterval Number of milliseconds between each scroll while a tab scroll button is + * continuously pressed (defaults to 400). + */ + scrollRepeatInterval : 400, + /** + * @cfg {Float} scrollDuration The number of milliseconds that each scroll animation should last (defaults + * to .35). Only applies when {@link #animScroll} = true. + */ + scrollDuration : 0.35, + /** + * @cfg {Boolean} animScroll True to animate tab scrolling so that hidden tabs slide smoothly into view (defaults + * to true). Only applies when {@link #enableTabScroll} = true. + */ + animScroll : true, + /** + * @cfg {String} tabPosition The position where the tab strip should be rendered (defaults to 'top'). + * The only other supported value is 'bottom'. Note: tab scrolling is only supported for + * tabPosition: 'top'. + */ + tabPosition : 'top', + /** + * @cfg {String} baseCls The base CSS class applied to the panel (defaults to 'x-tab-panel'). + */ + baseCls : 'x-tab-panel', + /** + * @cfg {Boolean} autoTabs + *true to query the DOM for any divs with a class of 'x-tab' to be automatically converted + * to tabs and added to this panel (defaults to false). Note that the query will be executed within + * the scope of the container element only (so that multiple tab panels from markup can be supported via this + * method).
+ *This method is only possible when the markup is structured correctly as a container with nested divs + * containing the class 'x-tab'. To create TabPanels without these limitations, or to pull tab content + * from other elements on the page, see the example at the top of the class for generating tabs from markup.
+ *There are a couple of things to note when using this method:
+var tabs = new Ext.TabPanel({
+ applyTo: 'my-tabs',
+ activeTab: 0,
+ deferredRender: false,
+ autoTabs: true
+});
+
+// This markup will be converted to a TabPanel from the code above
+<div id="my-tabs">
+ <div class="x-tab" title="Tab 1">A simple tab</div>
+ <div class="x-tab" title="Tab 2">Another one</div>
+</div>
+
+ */
+ autoTabs : false,
+ /**
+ * @cfg {String} autoTabSelector The CSS selector used to search for tabs in existing markup when
+ * {@link #autoTabs} = true (defaults to 'div.x-tab'). This can be any valid selector
+ * supported by {@link Ext.DomQuery#select}. Note that the query will be executed within the scope of this
+ * tab panel only (so that multiple tab panels from markup can be supported on a page).
+ */
+ autoTabSelector : 'div.x-tab',
+ /**
+ * @cfg {String/Number} activeTab A string id or the numeric index of the tab that should be initially
+ * activated on render (defaults to none).
+ */
+ activeTab : null,
+ /**
+ * @cfg {Number} tabMargin The number of pixels of space to calculate into the sizing and scrolling of
+ * tabs. If you change the margin in CSS, you will need to update this value so calculations are correct
+ * with either {@link #resizeTabs} or scrolling tabs. (defaults to 2)
+ */
+ tabMargin : 2,
+ /**
+ * @cfg {Boolean} plain true to render the tab strip without a background container image
+ * (defaults to false).
+ */
+ plain : false,
+ /**
+ * @cfg {Number} wheelIncrement For scrolling tabs, the number of pixels to increment on mouse wheel
+ * scrolling (defaults to 20).
+ */
+ wheelIncrement : 20,
+
+ /*
+ * This is a protected property used when concatenating tab ids to the TabPanel id for internal uniqueness.
+ * It does not generally need to be changed, but can be if external code also uses an id scheme that can
+ * potentially clash with this one.
+ */
+ idDelimiter : '__',
+
+ // private
+ itemCls : 'x-tab-item',
+
+ // private config overrides
+ elements : 'body',
+ headerAsText : false,
+ frame : false,
+ hideBorders :true,
+
+ // private
+ initComponent : function(){
+ this.frame = false;
+ Ext.TabPanel.superclass.initComponent.call(this);
+ this.addEvents(
+ /**
+ * @event beforetabchange
+ * Fires before the active tab changes. Handlers can return false to cancel the tab change.
+ * @param {TabPanel} this
+ * @param {Panel} newTab The tab being activated
+ * @param {Panel} currentTab The current active tab
+ */
+ 'beforetabchange',
+ /**
+ * @event tabchange
+ * Fires after the active tab has changed.
+ * @param {TabPanel} this
+ * @param {Panel} tab The new active tab
+ */
+ 'tabchange',
+ /**
+ * @event contextmenu
+ * Relays the contextmenu event from a tab selector element in the tab strip.
+ * @param {TabPanel} this
+ * @param {Panel} tab The target tab
+ * @param {EventObject} e
+ */
+ 'contextmenu'
+ );
+ /**
+ * @cfg {Object} layoutConfig
+ * TabPanel implicitly uses {@link Ext.layout.CardLayout} as its layout manager.
+ * layoutConfig
may be used to configure this layout manager.
+ * {@link #deferredRender}
and {@link #layoutOnTabChange}
+ * configured on the TabPanel will be applied as configs to the layout manager.
+ */
+ this.setLayout(new Ext.layout.CardLayout(Ext.apply({
+ layoutOnCardChange: this.layoutOnTabChange,
+ deferredRender: this.deferredRender
+ }, this.layoutConfig)));
+
+ if(this.tabPosition == 'top'){
+ this.elements += ',header';
+ this.stripTarget = 'header';
+ }else {
+ this.elements += ',footer';
+ this.stripTarget = 'footer';
+ }
+ if(!this.stack){
+ this.stack = Ext.TabPanel.AccessStack();
+ }
+ this.initItems();
+ },
+
+ // private
+ onRender : function(ct, position){
+ Ext.TabPanel.superclass.onRender.call(this, ct, position);
+
+ if(this.plain){
+ var pos = this.tabPosition == 'top' ? 'header' : 'footer';
+ this[pos].addClass('x-tab-panel-'+pos+'-plain');
+ }
+
+ var st = this[this.stripTarget];
+
+ this.stripWrap = st.createChild({cls:'x-tab-strip-wrap', cn:{
+ tag:'ul', cls:'x-tab-strip x-tab-strip-'+this.tabPosition}});
+
+ var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
+ this.stripSpacer = st.createChild({cls:'x-tab-strip-spacer'}, beforeEl);
+ this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
+
+ this.edge = this.strip.createChild({tag:'li', cls:'x-tab-edge'});
+ this.strip.createChild({cls:'x-clear'});
+
+ this.body.addClass('x-tab-panel-body-'+this.tabPosition);
+
+ /**
+ * @cfg {Template/XTemplate} itemTpl (Optional) A {@link Ext.Template Template} or + * {@link Ext.XTemplate XTemplate} which may be provided to process the data object returned from + * {@link #getTemplateArgs} to produce a clickable selector element in the tab strip.
+ *The main element created should be a <li> element. In order for a click event on + * a selector element to be connected to its item, it must take its id from the TabPanel's + * native {@link #getTemplateArgs}.
+ *The child element which contains the title text must be marked by the CSS class + * x-tab-strip-inner.
+ *To enable closability, the created element should contain an element marked by the CSS class + * x-tab-strip-close.
+ *If a custom itemTpl is supplied, it is the developer's responsibility to create CSS + * style rules to create the desired appearance.
+ * Below is an example of how to create customized tab selector items:
+new Ext.TabPanel({
+ renderTo: document.body,
+ minTabWidth: 115,
+ tabWidth: 135,
+ enableTabScroll: true,
+ width: 600,
+ height: 250,
+ defaults: {autoScroll:true},
+ itemTpl: new Ext.XTemplate(
+ '<li class="{cls}" id="{id}" style="overflow:hidden">',
+ '<tpl if="closable">',
+ '<a class="x-tab-strip-close" onclick="return false;"></a>',
+ '</tpl>',
+ '<a class="x-tab-right" href="#" onclick="return false;" style="padding-left:6px">',
+ '<em class="x-tab-left">',
+ '<span class="x-tab-strip-inner">',
+ '<img src="{src}" style="float:left;margin:3px 3px 0 0">',
+ '<span style="margin-left:20px" class="x-tab-strip-text {iconCls}">{text} {extra}</span>',
+ '</span>',
+ '</em>',
+ '</a>',
+ '</li>'
+ ),
+ getTemplateArgs: function(item) {
+// Call the native method to collect the base data. Like the ID!
+ var result = Ext.TabPanel.prototype.getTemplateArgs.call(this, item);
+
+// Add stuff used in our template
+ return Ext.apply(result, {
+ closable: item.closable,
+ src: item.iconSrc,
+ extra: item.extraText || ''
+ });
+ },
+ items: [{
+ title: 'New Tab 1',
+ iconSrc: '../shared/icons/fam/grid.png',
+ html: 'Tab Body 1',
+ closable: true
+ }, {
+ title: 'New Tab 2',
+ iconSrc: '../shared/icons/fam/grid.png',
+ html: 'Tab Body 2',
+ extraText: 'Extra stuff in the tab button'
+ }]
+});
+
+ */
+ if(!this.itemTpl){
+ var tt = new Ext.Template(
+ 'Provides template arguments for rendering a tab selector item in the tab strip.
+ *This method returns an object hash containing properties used by the TabPanel's {@link #itemTpl} + * to create a formatted, clickable tab selector element. The properties which must be returned + * are:
{@link Ext.Component#itemId itemId}
+ * or {@link Ext.Component#id id}
of the child component {@link Ext.Container#items items}
propertyFor additional information see {@link Ext.util.MixedCollection#get}. + */ + setActiveTab : function(item){ + item = this.getComponent(item); + if(!item || this.fireEvent('beforetabchange', this, item, this.activeTab) === false){ + return; + } + if(!this.rendered){ + this.activeTab = item; + return; + } + if(this.activeTab != item){ + if(this.activeTab){ + var oldEl = this.getTabEl(this.activeTab); + if(oldEl){ + Ext.fly(oldEl).removeClass('x-tab-strip-active'); + } + this.activeTab.fireEvent('deactivate', this.activeTab); + } + var el = this.getTabEl(item); + Ext.fly(el).addClass('x-tab-strip-active'); + this.activeTab = item; + this.stack.add(item); + + this.layout.setActiveItem(item); + if(this.scrolling){ + this.scrollToTab(item, this.animScroll); + } + + item.fireEvent('activate', item); + this.fireEvent('tabchange', this, item); + } + }, + +
/** + * Gets the currently active tab. + * @return {Panel} The active tab + */ + getActiveTab : function(){ + return this.activeTab || null; + }, + + /** + * Gets the specified tab by id. + * @param {String} id The tab id + * @return {Panel} The tab + */ + getItem : function(item){ + return this.getComponent(item); + }, + + // private + autoScrollTabs : function(){ + this.pos = this.tabPosition=='bottom' ? this.footer : this.header; + var count = this.items.length; + var ow = this.pos.dom.offsetWidth; + var tw = this.pos.dom.clientWidth; + + var wrap = this.stripWrap; + var wd = wrap.dom; + var cw = wd.offsetWidth; + var pos = this.getScrollPos(); + var l = this.edge.getOffsetsTo(this.stripWrap)[0] + pos; + + if(!this.enableTabScroll || count < 1 || cw < 20){ // 20 to prevent display:none issues + return; + } + if(l <= tw){ + wd.scrollLeft = 0; + wrap.setWidth(tw); + if(this.scrolling){ + this.scrolling = false; + this.pos.removeClass('x-tab-scrolling'); + this.scrollLeft.hide(); + this.scrollRight.hide(); + // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari + if(Ext.isAir || Ext.isWebKit){ + wd.style.marginLeft = ''; + wd.style.marginRight = ''; + } + } + }else{ + if(!this.scrolling){ + this.pos.addClass('x-tab-scrolling'); + // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari + if(Ext.isAir || Ext.isWebKit){ + wd.style.marginLeft = '18px'; + wd.style.marginRight = '18px'; + } + } + tw -= wrap.getMargins('lr'); + wrap.setWidth(tw > 20 ? tw : 20); + if(!this.scrolling){ + if(!this.scrollLeft){ + this.createScrollers(); + }else{ + this.scrollLeft.show(); + this.scrollRight.show(); + } + } + this.scrolling = true; + if(pos > (l-tw)){ // ensure it stays within bounds + wd.scrollLeft = l-tw; + }else{ // otherwise, make sure the active tab is still visible + this.scrollToTab(this.activeTab, false); + } + this.updateScrollButtons(); + } + }, + + // private + createScrollers : function(){ + this.pos.addClass('x-tab-scrolling-' + this.tabPosition); + var h = this.stripWrap.dom.offsetHeight; + + // left + var sl = this.pos.insertFirst({ + cls:'x-tab-scroller-left' + }); + sl.setHeight(h); + sl.addClassOnOver('x-tab-scroller-left-over'); + this.leftRepeater = new Ext.util.ClickRepeater(sl, { + interval : this.scrollRepeatInterval, + handler: this.onScrollLeft, + scope: this + }); + this.scrollLeft = sl; + + // right + var sr = this.pos.insertFirst({ + cls:'x-tab-scroller-right' + }); + sr.setHeight(h); + sr.addClassOnOver('x-tab-scroller-right-over'); + this.rightRepeater = new Ext.util.ClickRepeater(sr, { + interval : this.scrollRepeatInterval, + handler: this.onScrollRight, + scope: this + }); + this.scrollRight = sr; + }, + + // private + getScrollWidth : function(){ + return this.edge.getOffsetsTo(this.stripWrap)[0] + this.getScrollPos(); + }, + + // private + getScrollPos : function(){ + return parseInt(this.stripWrap.dom.scrollLeft, 10) || 0; + }, + + // private + getScrollArea : function(){ + return parseInt(this.stripWrap.dom.clientWidth, 10) || 0; + }, + + // private + getScrollAnim : function(){ + return {duration:this.scrollDuration, callback: this.updateScrollButtons, scope: this}; + }, + + // private + getScrollIncrement : function(){ + return this.scrollIncrement || (this.resizeTabs ? this.lastTabWidth+2 : 100); + }, + + /** + * Scrolls to a particular tab if tab scrolling is enabled + * @param {Panel} item The item to scroll to + * @param {Boolean} animate True to enable animations + */ + + scrollToTab : function(item, animate){ + if(!item){ return; } + var el = this.getTabEl(item); + var pos = this.getScrollPos(), area = this.getScrollArea(); + var left = Ext.fly(el).getOffsetsTo(this.stripWrap)[0] + pos; + var right = left + el.offsetWidth; + if(left < pos){ + this.scrollTo(left, animate); + }else if(right > (pos + area)){ + this.scrollTo(right - area, animate); + } + }, + + // private + scrollTo : function(pos, animate){ + this.stripWrap.scrollTo('left', pos, animate ? this.getScrollAnim() : false); + if(!animate){ + this.updateScrollButtons(); + } + }, + + onWheel : function(e){ + var d = e.getWheelDelta()*this.wheelIncrement*-1; + e.stopEvent(); + + var pos = this.getScrollPos(); + var newpos = pos + d; + var sw = this.getScrollWidth()-this.getScrollArea(); + + var s = Math.max(0, Math.min(sw, newpos)); + if(s != pos){ + this.scrollTo(s, false); + } + }, + + // private + onScrollRight : function(){ + var sw = this.getScrollWidth()-this.getScrollArea(); + var pos = this.getScrollPos(); + var s = Math.min(sw, pos + this.getScrollIncrement()); + if(s != pos){ + this.scrollTo(s, this.animScroll); + } + }, + + // private + onScrollLeft : function(){ + var pos = this.getScrollPos(); + var s = Math.max(0, pos - this.getScrollIncrement()); + if(s != pos){ + this.scrollTo(s, this.animScroll); + } + }, + + // private + updateScrollButtons : function(){ + var pos = this.getScrollPos(); + this.scrollLeft[pos === 0 ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled'); + this.scrollRight[pos >= (this.getScrollWidth()-this.getScrollArea()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled'); + }, + + // private + beforeDestroy : function() { + if(this.items){ + this.items.each(function(item){ + if(item && item.tabEl){ + Ext.get(item.tabEl).removeAllListeners(); + item.tabEl = null; + } + }, this); + } + if(this.strip){ + this.strip.removeAllListeners(); + } + Ext.TabPanel.superclass.beforeDestroy.apply(this); + } + + /** + * @cfg {Boolean} collapsible + * @hide + */ + /** + * @cfg {String} header + * @hide + */ + /** + * @cfg {Boolean} headerAsText + * @hide + */ + /** + * @property header + * @hide + */ + /** + * @property title + * @hide + */ + /** + * @cfg {Array} tools + * @hide + */ + /** + * @cfg {Array} toolTemplate + * @hide + */ + /** + * @cfg {Boolean} hideCollapseTool + * @hide + */ + /** + * @cfg {Boolean} titleCollapse + * @hide + */ + /** + * @cfg {Boolean} collapsed + * @hide + */ + /** + * @cfg {String} layout + * @hide + */ + /** + * @cfg {Boolean} preventBodyReset + * @hide + */ +}); +Ext.reg('tabpanel', Ext.TabPanel); + +/** + * See {@link #setActiveTab}. Sets the specified tab as the active tab. This method fires + * the {@link #beforetabchange} event which can return false to cancel the tab change. + * @param {String/Panel} tab The id or tab Panel to activate + * @method activate + */ +Ext.TabPanel.prototype.activate = Ext.TabPanel.prototype.setActiveTab; + +// private utility class used by TabPanel +Ext.TabPanel.AccessStack = function(){ + var items = []; + return { + add : function(item){ + items.push(item); + if(items.length > 10){ + items.shift(); + } + }, + + remove : function(item){ + var s = []; + for(var i = 0, len = items.length; i < len; i++) { + if(items[i] != item){ + s.push(items[i]); + } + } + items = s; + }, + + next : function(){ + return items.pop(); + } + }; +}; + + \ No newline at end of file