Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / examples / ux / GroupTabPanel.js
index 8033b7e..48377be 100644 (file)
-/*!
- * Ext JS Library 3.0.3
- * Copyright(c) 2006-2009 Ext JS, LLC
- * licensing@extjs.com
- * http://www.extjs.com/license
+/*
+
+This file is part of Ext JS 4
+
+Copyright (c) 2011 Sencha Inc
+
+Contact:  http://www.sencha.com/contact
+
+GNU General Public License Usage
+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.
+
+If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
+
+*/
+/**
+ * @author Nicolas Ferrero
+ * @class Ext.ux.GroupTabPanel
+ * @extends Ext.Container
+ * A TabPanel with grouping support.
  */
-Ext.ns('Ext.ux');\r
-\r
-Ext.ux.GroupTabPanel = Ext.extend(Ext.TabPanel, {\r
-    tabPosition: 'left',\r
-    \r
-    alternateColor: false,\r
-    \r
-    alternateCls: 'x-grouptabs-panel-alt',\r
-    \r
-    defaultType: 'grouptab',\r
-    \r
-    deferredRender: false,\r
-    \r
-    activeGroup : null,\r
-    \r
-    initComponent: function(){\r
-        Ext.ux.GroupTabPanel.superclass.initComponent.call(this);\r
-        \r
-        this.addEvents(\r
-            'beforegroupchange',\r
-            'groupchange'\r
-        );\r
-        this.elements = 'body,header';\r
-        this.stripTarget = 'header';\r
-        \r
-        this.tabPosition = this.tabPosition == 'right' ? 'right' : 'left';\r
-        \r
-        this.addClass('x-grouptabs-panel');\r
-        \r
-        if (this.tabStyle && this.tabStyle != '') {\r
-            this.addClass('x-grouptabs-panel-' + this.tabStyle);\r
-        }\r
-        \r
-        if (this.alternateColor) {\r
-            this.addClass(this.alternateCls);\r
-        }\r
-        \r
-        this.on('beforeadd', function(gtp, item, index){\r
-            this.initGroup(item, index);\r
-        });          \r
-    },\r
-    \r
-    initEvents : function() {\r
-        this.mon(this.strip, 'mousedown', this.onStripMouseDown, this);\r
-    },\r
-        \r
-    onRender: function(ct, position){\r
-        Ext.TabPanel.superclass.onRender.call(this, ct, position);\r
-        if(this.plain){\r
-            var pos = this.tabPosition == 'top' ? 'header' : 'footer';\r
-            this[pos].addClass('x-tab-panel-'+pos+'-plain');\r
-        }\r
-\r
-        var st = this[this.stripTarget];\r
-\r
-        this.stripWrap = st.createChild({cls:'x-tab-strip-wrap ', cn:{\r
-            tag:'ul', cls:'x-grouptabs-strip x-grouptabs-tab-strip-'+this.tabPosition}});\r
-\r
-        var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);\r
-        this.strip = new Ext.Element(this.stripWrap.dom.firstChild);\r
-\r
-        this.header.addClass('x-grouptabs-panel-header');\r
-        this.bwrap.addClass('x-grouptabs-bwrap');\r
-        this.body.addClass('x-tab-panel-body-'+this.tabPosition + ' x-grouptabs-panel-body');\r
-\r
-        if (!this.groupTpl) {\r
-            var tt = new Ext.Template(\r
-                '<li class="{cls}" id="{id}">', \r
-                '<a class="x-grouptabs-expand" onclick="return false;"></a>', \r
-                '<a class="x-grouptabs-text {iconCls}" href="#" onclick="return false;">',\r
-                '<span>{text}</span></a>', \r
-                '</li>'\r
-            );\r
-            tt.disableFormats = true;\r
-            tt.compile();\r
-            Ext.ux.GroupTabPanel.prototype.groupTpl = tt;\r
-        }\r
-        this.items.each(this.initGroup, this);\r
-    },\r
-    \r
-    afterRender: function(){\r
-        Ext.ux.GroupTabPanel.superclass.afterRender.call(this);\r
-        \r
-        this.tabJoint = Ext.fly(this.body.dom.parentNode).createChild({\r
-            cls: 'x-tab-joint'\r
-        });\r
-        \r
-        this.addClass('x-tab-panel-' + this.tabPosition);\r
-        this.header.setWidth(this.tabWidth);\r
-        \r
-        if (this.activeGroup !== undefined) {\r
-            var group = (typeof this.activeGroup == 'object') ? this.activeGroup : this.items.get(this.activeGroup);\r
-            delete this.activeGroup;\r
-            this.setActiveGroup(group);\r
-            group.setActiveTab(group.getMainItem());\r
-        }\r
-    },\r
-\r
-    getGroupEl : Ext.TabPanel.prototype.getTabEl,\r
-        \r
-    // private\r
-    findTargets: function(e){\r
-        var item = null,\r
-            itemEl = e.getTarget('li', this.strip);\r
-        if (itemEl) {\r
-            item = this.findById(itemEl.id.split(this.idDelimiter)[1]);\r
-            if (item.disabled) {\r
-                return {\r
-                    expand: null,\r
-                    item: null,\r
-                    el: null\r
-                };\r
-            }\r
-        }\r
-        return {\r
-            expand: e.getTarget('.x-grouptabs-expand', this.strip),\r
-            isGroup: !e.getTarget('ul.x-grouptabs-sub', this.strip),\r
-            item: item,\r
-            el: itemEl\r
-        };\r
-    },\r
-    \r
-    // private\r
-    onStripMouseDown: function(e){\r
-        if (e.button != 0) {\r
-            return;\r
-        }\r
-        e.preventDefault();\r
-        var t = this.findTargets(e);\r
-        if (t.expand) {\r
-            this.toggleGroup(t.el);\r
-        }\r
-        else if (t.item) {\r
-            if(t.isGroup) {\r
-                t.item.setActiveTab(t.item.getMainItem());\r
-            }\r
-            else {\r
-                t.item.ownerCt.setActiveTab(t.item);\r
-            }\r
-        }\r
-    },\r
-    \r
-    expandGroup: function(groupEl){\r
-        if(groupEl.isXType) {\r
-            groupEl = this.getGroupEl(groupEl);\r
-        }\r
-        Ext.fly(groupEl).addClass('x-grouptabs-expanded');\r
-    },\r
-    \r
-    toggleGroup: function(groupEl){\r
-        if(groupEl.isXType) {\r
-            groupEl = this.getGroupEl(groupEl);\r
-        }        \r
-        Ext.fly(groupEl).toggleClass('x-grouptabs-expanded');\r
-        this.syncTabJoint();\r
-    },    \r
-    \r
-    syncTabJoint: function(groupEl){\r
-        if (!this.tabJoint) {\r
-            return;\r
-        }\r
-        \r
-        groupEl = groupEl || this.getGroupEl(this.activeGroup);\r
-        if(groupEl) {\r
-            this.tabJoint.setHeight(Ext.fly(groupEl).getHeight() - 2); \r
-            \r
-            var y = Ext.isGecko2 ? 0 : 1;\r
-            if (this.tabPosition == 'left'){\r
-                this.tabJoint.alignTo(groupEl, 'tl-tr', [-2,y]);\r
-            }\r
-            else {\r
-                this.tabJoint.alignTo(groupEl, 'tr-tl', [1,y]);\r
-            }           \r
-        }\r
-        else {\r
-            this.tabJoint.hide();\r
-        }\r
-    },\r
-    \r
-    getActiveTab : function() {\r
-        if(!this.activeGroup) return null;\r
-        return this.activeGroup.getTabEl(this.activeGroup.activeTab) || null;  \r
-    },\r
-    \r
-    onResize: function(){\r
-        Ext.ux.GroupTabPanel.superclass.onResize.apply(this, arguments);\r
-        this.syncTabJoint();\r
-    },\r
-    \r
-    createCorner: function(el, pos){\r
-        return Ext.fly(el).createChild({\r
-            cls: 'x-grouptabs-corner x-grouptabs-corner-' + pos\r
-        });\r
-    },\r
-    \r
-    initGroup: function(group, index){\r
-        var before = this.strip.dom.childNodes[index],   \r
-            p = this.getTemplateArgs(group);\r
-        if (index === 0) {\r
-            p.cls += ' x-tab-first';\r
-        }\r
-        p.cls += ' x-grouptabs-main';\r
-        p.text = group.getMainItem().title;\r
-        \r
-        var el = before ? this.groupTpl.insertBefore(before, p) : this.groupTpl.append(this.strip, p),\r
-            tl = this.createCorner(el, 'top-' + this.tabPosition),\r
-            bl = this.createCorner(el, 'bottom-' + this.tabPosition);\r
-\r
-        if (group.expanded) {\r
-            this.expandGroup(el);\r
-        }\r
-\r
-        if (Ext.isIE6 || (Ext.isIE && !Ext.isStrict)){\r
-            bl.setLeft('-10px');\r
-            bl.setBottom('-5px');\r
-            tl.setLeft('-10px');\r
-            tl.setTop('-5px');\r
-        }\r
-\r
-        this.mon(group, {\r
-            scope: this,\r
-            changemainitem: this.onGroupChangeMainItem,\r
-            beforetabchange: this.onGroupBeforeTabChange\r
-        });\r
-    },\r
-    \r
-    setActiveGroup : function(group) {\r
-        group = this.getComponent(group);\r
-        if(!group || this.fireEvent('beforegroupchange', this, group, this.activeGroup) === false){\r
-            return;\r
-        }\r
-        if(!this.rendered){\r
-            this.activeGroup = group;\r
-            return;\r
-        }\r
-        if(this.activeGroup != group){\r
-            if(this.activeGroup){\r
-                var oldEl = this.getGroupEl(this.activeGroup);\r
-                if(oldEl){\r
-                    Ext.fly(oldEl).removeClass('x-grouptabs-strip-active');\r
-                }\r
-                this.activeGroup.fireEvent('deactivate', this.activeGroup);\r
-            }\r
-\r
-            var groupEl = this.getGroupEl(group);\r
-            Ext.fly(groupEl).addClass('x-grouptabs-strip-active');\r
-                        \r
-            this.activeGroup = group;\r
-            this.stack.add(group);\r
-\r
-            this.layout.setActiveItem(group);\r
-            this.syncTabJoint(groupEl);\r
-\r
-            group.fireEvent('activate', group);\r
-            this.fireEvent('groupchange', this, group);\r
-        }        \r
-    },\r
-    \r
-    onGroupBeforeTabChange: function(group, newTab, oldTab){\r
-        if(group !== this.activeGroup || newTab !== oldTab) {\r
-            this.strip.select('.x-grouptabs-sub > li.x-grouptabs-strip-active', true).removeClass('x-grouptabs-strip-active');\r
-        } \r
-        \r
-        this.expandGroup(this.getGroupEl(group));\r
-        this.setActiveGroup(group);\r
-    },\r
-    \r
-    getFrameHeight: function(){\r
-        var h = this.el.getFrameWidth('tb');\r
-        h += (this.tbar ? this.tbar.getHeight() : 0) +\r
-        (this.bbar ? this.bbar.getHeight() : 0);\r
-        \r
-        return h;\r
-    },\r
-    \r
-    adjustBodyWidth: function(w){\r
-        return w - this.tabWidth;\r
-    }\r
-});\r
-\r
-Ext.reg('grouptabpanel', Ext.ux.GroupTabPanel);
\ No newline at end of file
+Ext.define('Ext.ux.GroupTabPanel', {
+    extend: 'Ext.Container',
+
+    alias: 'widget.grouptabpanel',
+
+    requires:[
+        'Ext.data.*',
+        'Ext.tree.*',
+        'Ext.layout.*'
+    ],
+
+    baseCls : Ext.baseCSSPrefix + 'grouptabpanel',
+
+    initComponent: function(config) {
+        var me = this,
+            items = [];
+
+        Ext.apply(me, config);
+
+        me.store = me.createItemsStore();
+        me.layout = {
+            type: 'hbox',
+            pack: 'start',
+            align: 'stretch'
+        };
+        me.defaults = {
+            border: false
+        };
+
+        me.items = Ext.each(me.items, function(item) {
+            items.push(item.items);
+        });
+
+        me.items = [{
+            xtype: 'treepanel',
+            cls: 'x-tree-panel x-grouptabbar',
+            width: 150,
+            rootVisible: false,
+            height: 400,
+            store: me.store,
+            hideHeaders: true,
+            useArrows: true,
+            animate: false,
+            viewConfig: {
+                overItemCls: ''
+            },
+            columns: [{
+                xtype: 'treecolumn',
+                sortable: false,
+                dataIndex: 'text',
+                flex: 1,
+                renderer: function (value, cell, node, idx1, idx2, store, tree) {
+                    var cls = '';
+
+                    if (!node.data.activeGroup) {
+                        cls += ' x-inactive-group';
+                    } else if (node.parentNode && node.parentNode.parentNode === null) {
+                        cls += ' x-grouptab-first';
+                        if (node.previousSibling) {
+                            cls += ' x-grouptab-prev';
+                        }
+                    } else if (node.nextSibling === null) {
+                        cls += ' x-grouptab-last';
+                    } else {
+                        cls += ' x-grouptab-center';
+                    }
+                    if (node.data.activeTab) {
+                            cls += ' x-active-tab';
+                    }
+                    cell.tdCls= 'x-grouptab'+ cls;
+
+                    return value;
+                }
+             }]
+        },{
+            xtype: 'container',
+            flex: 1,
+            layout: 'card',
+            activeItem: me.mainItem,
+            baseCls: Ext.baseCSSPrefix + 'grouptabcontainer',
+            items: items
+        }];
+
+        me.addEvents(
+            /**
+             * @event beforetabchange
+             * Fires before a tab change (activated by {@link #setActiveTab}). Return false in any listener to cancel
+             * the tabchange
+             * @param {Ext.ux.GroupTabPanel} grouptabPanel The GroupTabPanel
+             * @param {Ext.Component} newCard The card that is about to be activated
+             * @param {Ext.Component} oldCard The card that is currently active
+             */
+            'beforetabchange',
+
+            /**
+             * @event tabchange
+             * Fires when a new tab has been activated (activated by {@link #setActiveTab}).
+             * @param {Ext.ux.GroupTabPanel} grouptabPanel The GroupTabPanel
+             * @param {Ext.Component} newCard The newly activated item
+             * @param {Ext.Component} oldCard The previously active item
+             */
+            'tabchange',
+
+            /**
+             * @event beforegroupchange
+             * Fires before a group change (activated by {@link #setActiveGroup}). Return false in any listener to cancel
+             * the groupchange
+             * @param {Ext.ux.GroupTabPanel} grouptabPanel The GroupTabPanel
+             * @param {Ext.Component} newGroup The root group card that is about to be activated
+             * @param {Ext.Component} oldGroup The root group card that is currently active
+             */
+            'beforegroupchange',
+
+            /**
+             * @event groupchange
+             * Fires when a new group has been activated (activated by {@link #setActiveGroup}).
+             * @param {Ext.ux.GroupTabPanel} grouptabPanel The GroupTabPanel
+             * @param {Ext.Component} newGroup The newly activated root group item
+             * @param {Ext.Component} oldGroup The previously active root group item
+             */
+            'groupchange'
+        );
+
+        me.callParent(arguments);
+        me.setActiveTab(me.activeTab);
+        me.setActiveGroup(me.activeGroup);
+        me.mon(me.down('treepanel').getSelectionModel(), 'select', me.onNodeSelect, me);
+    },
+
+    /**
+     * @private
+     * Node selection listener.
+     */
+    onNodeSelect: function (selModel, node) {
+        var me = this,
+            currentNode = me.store.getRootNode(),
+            parent;
+
+        if (node.parentNode && node.parentNode.parentNode === null) {
+            parent = node;
+        } else {
+            parent = node.parentNode;
+        }
+
+        if (me.setActiveGroup(parent.get('id')) === false || me.setActiveTab(node.get('id')) === false) {
+            return false;
+        }
+
+        while(currentNode) {
+            currentNode.set('activeTab', false);
+            currentNode.set('activeGroup', false);
+            currentNode = currentNode.firstChild || currentNode.nextSibling || currentNode.parentNode.nextSibling;
+        }
+
+        parent.set('activeGroup', true);
+
+        parent.eachChild(function(child) {
+
+            child.set('activeGroup', true);
+        });
+        node.set('activeTab', true);
+        selModel.view.refresh();
+    },
+
+    /**
+     * Makes the given component active (makes it the visible card in the GroupTabPanel's CardLayout)
+     * @param {Ext.Component} cmp The component to make active
+     */
+    setActiveTab: function(cmp) {
+        var me = this,
+            newTab = cmp,
+            oldTab;
+
+        if(Ext.isString(cmp)) {
+            newTab = Ext.getCmp(newTab);
+        }
+
+        if (newTab === me.activeTab) {
+            return false;
+        }
+
+        oldTab = me.activeTab;
+        if (me.fireEvent('beforetabchange', me, newTab, oldTab) !== false) {
+             me.activeTab = newTab;
+             if (me.rendered) {
+                 me.down('container[baseCls=' + Ext.baseCSSPrefix + 'grouptabcontainer' + ']').getLayout().setActiveItem(newTab);
+             }
+             me.fireEvent('tabchange', me, newTab, oldTab);
+         }
+         return true;
+    },
+
+    /**
+     * Makes the given group active
+     * @param {Ext.Component} cmp The root component to make active.
+     */
+    setActiveGroup: function(cmp) {
+        var me = this,
+            newGroup = cmp,
+            oldGroup;
+
+        if(Ext.isString(cmp)) {
+            newGroup = Ext.getCmp(newGroup);
+        }
+
+        if (newGroup === me.activeGroup) {
+            return true;
+        }
+
+        oldGroup = me.activeGroup;
+        if (me.fireEvent('beforegroupchange', me, newGroup, oldGroup) !== false) {
+             me.activeGroup = newGroup;
+             me.fireEvent('groupchange', me, newGroup, oldGroup);
+         } else {
+             return false;
+         }
+         return true;
+    },
+
+    /**
+     * @private
+     * Creates the TreeStore used by the GroupTabBar.
+     */
+    createItemsStore: function() {
+        var me = this,
+            data = {
+            text:'.',
+            children: []
+        };
+        me.activeGroup = me.activeGroup || 0;
+
+        Ext.each(me.items, function(item, idx){
+            var items = item.items,
+                rootItem = (items[item.mainItem] || items[0]),
+                root = {
+                    children: []
+                };
+
+            if (!rootItem.id) {
+                rootItem.id = Ext.id();
+            }
+
+            root.id = rootItem.id;
+            root.text = rootItem.title;
+            root.iconCls = rootItem.iconCls;
+            delete rootItem.iconCls;
+            delete rootItem.title;
+            root.expanded = true;
+            root.activeGroup = (me.activeGroup === idx);
+            root.activeTab = root.activeGroup ? true : false;
+            if (root.activeTab) {
+                me.activeTab = root.id;
+            }
+
+            if (root.activeGroup) {
+                me.mainItem = item.mainItem || 0;
+                me.activeGroup = root.id;
+            }
+
+            Ext.each(item.items, function(childItem) {
+                if (!childItem.id) {
+                    childItem.id = Ext.id();
+                }
+                if(childItem.id !== root.id) {
+                    var child = {
+                        id: childItem.id,
+                        leaf: true,
+                        text: childItem.title,
+                        iconCls: childItem.iconCls,
+                        activeTab: false
+                    };
+                    delete childItem.title;
+                    delete childItem.iconCls;
+
+                    child.activeGroup = root.activeGroup;
+                    root.children.push(child);
+                }
+            }, me);
+
+            data.children.push(root);
+
+      }, me);
+
+       return Ext.create('Ext.data.TreeStore', {
+            fields: ['id', 'text', 'activeGroup', 'activeTab'],
+            root: {expanded: true},
+            proxy: {
+                type: 'memory',
+                data: data
+            }
+        });
+    },
+
+    /**
+     * Returns the item that is currently active inside this GroupTabPanel.
+     * @return {Ext.Component/Integer} The currently active item
+     */
+    getActiveTab: function() {
+        return this.activeTab;
+    },
+
+    /**
+     * Returns the root group item that is currently active inside this GroupTabPanel.
+     * @return {Ext.Component/Integer} The currently active root group item
+     */
+    getActiveGroup: function() {
+        return this.activeGroup;
+    }
+});
+