Hybrid View

  1. #1
    Sencha Premium Member tobiu's Avatar
    Join Date
    May 2007
    Location
    Munich (Germany)
    Posts
    2,695
    Vote Rating
    114
    tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all

      0  

    Default [3.1.1]Ext.ux.panel.DDTabPanel

    [3.1.1]Ext.ux.panel.DDTabPanel


    hi together,

    there are quite a lot of changes, but i think it runs now fine with ext 3.1.1.
    i had to remove:
    Code:
    onRemove : function(c){
        // DragSource cleanup on removed tabs
        Ext.destroy(c.ds.proxy, c.ds);
    because this caused massive errors (afterwards ext.fly caused firebug-errors on each mouseMove...

    please let me know, if this ux works fine for you too and definitely tell me if the changes cause memory leaks.

    kind regards,
    tobiu

    Code:
    Ext.namespace('Ext.ux.panel');
    
    /**
     * @class Ext.ux.panel.DDTabPanel
     * @extends Ext.TabPanel
     * @author
     *     Original by
     *         <a href="http://extjs.com/forum/member.php?u=22731">thommy</a> and
     *         <a href="http://extjs.com/forum/member.php?u=37284">rizjoj</a><br />
     *     Published and polished by: Mattias Buelens (<a href="http://extjs.com/forum/member.php?u=41421">Matti</a>)<br />
     *     With help from: <a href="http://extjs.com/forum/member.php?u=1459">mystix</a>
     *     Polished and debugged by: Tobias Uhlig (info@internetsachen.com) 04-25-2009
     *     Ported to Ext-3.1.1 by: Tobias Uhlig (info@internetsachen.com) 02-14-2010
     * @license Licensed under the terms of the Open Source <a href="http://www.gnu.org/licenses/lgpl.html">LGPL 3.0 license</a>. Commercial use is permitted to the extent that the code/component(s) do NOT become part of another Open Source or Commercially licensed development library or toolkit without explicit permission.
     * @version 2.0.0 (Feb 14, 2010)
     */
    Ext.ux.panel.DDTabPanel = Ext.extend(Ext.TabPanel, {
        /**
         * @cfg {Number} arrowOffsetX The horizontal offset for the drop arrow indicator, in pixels (defaults to -9).
         */
        arrowOffsetX: -9,
        /**
         * @cfg {Number} arrowOffsetY The vertical offset for the drop arrow indicator, in pixels (defaults to -8).
         */
        arrowOffsetY: -8,
    
        // Assign the drag and drop group id
        /** @private */
        initComponent: function(){
            Ext.ux.panel.DDTabPanel.superclass.initComponent.call(this);
            if(!this.ddGroupId) this.ddGroupId = 'dd-tabpanel-group-' + Ext.ux.panel.DDTabPanel.superclass.getId.call(this);
        },
    
        // Declare the tab panel as a drop target
        /** @private */
        afterRender: function(){
            Ext.ux.panel.DDTabPanel.superclass.afterRender.call(this);
            // Create a drop arrow indicator
            this.arrow = Ext.DomHelper.append(
                Ext.getBody(),
                '<div class="dd-arrow-down"></div>',
                true
            );
            this.arrow.hide();
           // Create a drop target for this tab panel
           var tabsDDGroup = this.ddGroupId;
           this.dd = new Ext.ux.panel.DDTabPanel.DropTarget(this, {
               ddGroup: tabsDDGroup
           });
    		
           // needed for the onRemove-Listener
            this.move = false;
        },
    
    	// Init the drag source after (!) rendering the tab
    	/** @private */
    	initTab: function(tab, index){
    		Ext.ux.panel.DDTabPanel.superclass.initTab.call(this, tab, index);
    
    		var id = this.id + '__' + tab.id;
    		// Enable dragging on all tabs by default
    		Ext.applyIf(tab, { allowDrag: true });
    
            // Extend the tab
    		Ext.apply(tab, {
                // Make this tab a drag source
    			ds: new Ext.dd.DragSource(id, {
    				 ddGroup      : this.ddGroupId
    				,dropEl       : tab
    				,dropElHeader : Ext.get(id, true)
    				,scroll       : false
    
                    // Update the drag proxy ghost element
    				,onStartDrag : function(){
    					if(this.dropEl.iconCls){
    		                
    		                var el = this.getProxy().getGhost().select(".x-tab-strip-text");
    		                el.addClass('x-panel-inline-icon');
    		                
    		                var proxyText = el.elements[0].innerHTML;
    		                proxyText = Ext.util.Format.stripTags(proxyText);
    		                el.elements[0].innerHTML = proxyText;
    
    						el.applyStyles({
    							paddingLeft: "20px"
    						});
    					}
    				}
    				
    		// Activate this tab on mouse up
                    // (Fixes bug which prevents a tab from being activated by clicking it)
                    ,onMouseUp: function(event){
                        if(this.dropEl.ownerCt.move){
                            if(!this.dropEl.disabled && this.dropEl.ownerCt.activeTab == null){
                                this.dropEl.ownerCt.setActiveTab(this.dropEl);
                            }
                            this.dropEl.ownerCt.move = false;
                            return;
                        }
                        if(!this.dropEl.isVisible() && !this.dropEl.disabled){
                            this.dropEl.show();
                        }
                    }
    			})
    			// Method to enable dragging
    			,enableTabDrag: function(){
    				this.allowDrag = true;
    				return this.ds.unlock();
    			}
    			// Method to disable dragging
    			,disableTabDrag: function(){
    				this.allowDrag = false;
    				return this.ds.lock();
    			}
    		});
    
    		// Initial dragging state
    		if(tab.allowDrag){
    			tab.enableTabDrag();
    		}else{
    			tab.disableTabDrag();
    		}
    	}
    
    	/** @private */
    	,onRemove : function(c){
            var te = Ext.get(c.tabEl);
            // check if the tabEl exists, it won't if the tab isn't rendered
            if(te){
                // DragSource cleanup on removed tabs
                //Ext.destroy(c.ds.proxy, c.ds);
                te.select('a').removeAllListeners();
                Ext.destroy(te);
            }
            
            // ignore the remove-function of the TabPanel
            Ext.TabPanel.superclass.onRemove.call(this, c);
            
            this.stack.remove(c);
            delete c.tabEl;
            c.un('disable'     ,this.onItemDisabled     ,this);
            c.un('enable'      ,this.onItemEnabled      ,this);
            c.un('titlechange' ,this.onItemTitleChanged ,this);
            c.un('iconchange'  ,this.onItemIconChanged  ,this);
            c.un('beforeshow'  ,this.onBeforeShowItem   ,this);
    
            // if this.move, the active tab stays the active one
            if(c == this.activeTab){
                if(!this.move){
                    var next = this.stack.next();
                    if(next){
                        this.setActiveTab(next);
                    }else if(this.items.getCount() > 0){
                        this.setActiveTab(0);
                    }else{
                        this.activeTab = null;
                    }
                }
                else {
                    this.activeTab = null;
                }
            }
            if(!this.destroying){
                this.delegateUpdates();
            }
        }
    	
    
    	// DropTarget and arrow cleanup
    	/** @private */
    	,onDestroy: function(){
    		Ext.destroy(this.dd, this.arrow);
    		Ext.ux.panel.DDTabPanel.superclass.onDestroy.call(this);
    	}
    });
    
    // Ext.ux.panel.DDTabPanel.DropTarget
    // Implements the drop behavior of the tab panel
    /** @private */
    Ext.ux.panel.DDTabPanel.DropTarget = Ext.extend(Ext.dd.DropTarget, {
    	constructor: function(tabpanel, config){
    		this.tabpanel = tabpanel;
    		// The drop target is the tab strip wrap
    		Ext.ux.panel.DDTabPanel.DropTarget.superclass.constructor.call(this, tabpanel.stripWrap, config);
    	}
    
    	,notifyOver: function(dd, e, data){
    		var tabs = this.tabpanel.items;
    		var last = tabs.length;
    
    		if(!e.within(this.getEl()) || dd.dropEl == this.tabpanel){
    			return 'x-dd-drop-nodrop';
    		}
    
    		var larrow = this.tabpanel.arrow;
    
    		// Getting the absolute Y coordinate of the tabpanel
    		var tabPanelTop = this.el.getY();
    
    		var left, prevTab, tab;
    		var eventPosX = e.getPageX();
    
    		for(var i = 0; i < last; i++){
                prevTab = tab;
    			tab     = tabs.itemAt(i);
    			// Is this tab target of the drop operation?
    			var tabEl = tab.ds.dropElHeader;
    			// Getting the absolute X coordinate of the tab
    			var tabLeft = tabEl.getX();
    			// Get the middle of the tab
    			var tabMiddle = tabLeft + tabEl.dom.clientWidth / 2;
    
    			if(eventPosX <= tabMiddle){
    				left = tabLeft;
    				break;
    			}
    		}
    		
    		if(typeof left == 'undefined'){
    			var lastTab = tabs.itemAt(last - 1);
                if(lastTab == dd.dropEl)return 'x-dd-drop-nodrop';
    			var dom = lastTab.ds.dropElHeader.dom;
    			left = (new Ext.Element(dom).getX() + dom.clientWidth) + 3;
    		}
    		
    		else if(tab == dd.dropEl || prevTab == dd.dropEl){
                this.tabpanel.arrow.hide();
                return 'x-dd-drop-nodrop';
            }
    
    		larrow.setTop(tabPanelTop + this.tabpanel.arrowOffsetY).setLeft(left + this.tabpanel.arrowOffsetX).show();
    
    		return 'x-dd-drop-ok';
    	}
    
    	,notifyDrop: function(dd, e, data){
    		this.tabpanel.arrow.hide();
    		
    		// no parent into child
    		if(dd.dropEl == this.tabpanel){
    			return false;
    		}
    		var tabs      = this.tabpanel.items;
    		var eventPosX = e.getPageX();
    
    		for(var i = 0; i < tabs.length; i++){
                var tab = tabs.itemAt(i);
    			// Is this tab target of the drop operation?
    			var tabEl = tab.ds.dropElHeader;
    			// Getting the absolute X coordinate of the tab
    			var tabLeft = tabEl.getX();
    			// Get the middle of the tab
    			var tabMiddle = tabLeft + tabEl.dom.clientWidth / 2;
    			if(eventPosX <= tabMiddle) break;
    		}
    
            // do not insert at the same location
            if(tab == dd.dropEl || tabs.itemAt(i-1) == dd.dropEl){
                return false;
            }
            
    	dd.proxy.hide();
    
            // if tab stays in the same tabPanel
            if(dd.dropEl.ownerCt == this.tabpanel){
                if(i > tabs.indexOf(dd.dropEl))i--;
            }
    
            this.tabpanel.move = true;
    		var dropEl = dd.dropEl.ownerCt.remove(dd.dropEl, false);
            
    		this.tabpanel.insert(i, dropEl);
    		
    		return true;
    	}
    
    	,notifyOut: function(dd, e, data){
            this.tabpanel.arrow.hide();
    	}
    });
    
    Ext.reg('ddtabpanel', Ext.ux.panel.DDTabPanel);
    Best regards
    Tobias Uhlig
    __________

    S-CIRCLES Social Network Engine

  2. #2
    Ext JS Premium Member
    Join Date
    Oct 2007
    Posts
    83
    Vote Rating
    2
    miti is on a distinguished road

      0  

    Default


    Thanks for the update. Seems everything works fine except I have to override and comment out a line for the DragSource - otherwise I will get an error sometime when the drop is done - probably because dragsource is not properly destroyed?
    Code:
    ,afterRepair : function(){
    			        if(Ext.enableFx){
    			            //this.el.highlight(this.hlColor || "c3daf9");
    			        }
    			        this.dragging = false;
    			    }
    firebug trace:

    this.dom is undefined
    hasFxBlock()ext-all-debug.js (line 8611)
    queueFx(Object { name="o"}, Object { name="fn"})ext-all-debug.js (line 8618)
    highlight(Object { name="color"}, Object { name="o"})ext-all-debug.js (line 8135)
    afterRepair()DDTabPanel.js (line 109)
    afterRepair()ext-all-debug.js (line 30649)
    afterFx(Object { name="o"})ext-all-debug.js (line 8692)
    (?)()ext-all-debug.js (line 8446)
    apply()ext-base.js (line 7)
    apply()ext-base.js (line 7)
    fire()ext-all-debug.js (line 2415)
    apply()ext-base.js (line 7)
    fire()ext-all-debug.js (line 2415)
    apply(Object { name="r"}, Object { name="q"})ext-base.js (line 7)
    apply(Object { name="s"})ext-base.js (line 7)
    apply()ext-base.js (line 7)
    [Break on this error] var q = getQueue(this.dom.id);

  3. #3
    Sencha Premium Member tobiu's Avatar
    Join Date
    May 2007
    Location
    Munich (Germany)
    Posts
    2,695
    Vote Rating
    114
    tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all

      0  

    Default


    hi miti,

    tested quite a while and that error never occured to me.
    so, i need further information (or a small testcase) to track it down.

    the function afterRepair only gets called onInvalidDrop, working fine for me (tabs moving back with animation).

    if you need the override, i suggest a change to:
    Code:
    if(this.el)this.el.highlight(this.hlColor || "c3daf9");
    i changed the "no parent into child" condition of notifyDrop a bit for nested layouts, so that it is not possible to add a parent of any level into a child:

    Code:
    var parentEl = this.tabpanel.findParentBy(function(p){
        if(p.id == dd.dropEl.id)return true;
        return false;
    });
    
    // no parent of any level into child
    if(dd.dropEl == this.tabpanel || dd.dropEl == parentEl){
        return false;
    }
    kind regards,
    tobiu
    Best regards
    Tobias Uhlig
    __________

    S-CIRCLES Social Network Engine

  4. #4
    Ext JS Premium Member
    Join Date
    Oct 2007
    Posts
    83
    Vote Rating
    2
    miti is on a distinguished road

      0  

    Default


    Wow thanks for the quick reply. My case is a little complicated. The target panel is like a docking pane in eclipse that you can drag a tab to but will be hidden if no tab is docked. So I do some special code to handle it and maybe that cause problem. The weird thing is the problematic .afterRepair is called after a VALID drop.

    Code:
    if (this.el) this.el.highlight(this.hlColor || "c3daf9");
    won't help and this.el is some dragProxy. but

    Code:
    if (this.el.dom) this.el.highlight(this.hlColor || "c3daf9");
    fixed the problem. Obviously the dom is gone but the el is still there.

  5. #5
    Sencha Premium Member tobiu's Avatar
    Join Date
    May 2007
    Location
    Munich (Germany)
    Posts
    2,695
    Vote Rating
    114
    tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all tobiu is a name known to all

      0  

    Default


    it might be related to the point of the top-posting,
    i had to remove
    Code:
     Ext.destroy(c.ds.proxy, c.ds);
    which was resposible for cleaning up.
    i have more time next week and will try to find another solution (that line causes ext-fly to run amok...).


    kind regards,
    tobiu
    Best regards
    Tobias Uhlig
    __________

    S-CIRCLES Social Network Engine

  6. #6
    Sencha User
    Join Date
    Sep 2007
    Posts
    98
    Vote Rating
    1
    adam.jimenez is on a distinguished road

      0  

    Default


    Hi, thanks for this update.

    It has the same issue that the original DDtabpanel had. When you move a tab it removes it and re-inserts it. This is a problem for me as it triggers my beforeRemove handler.

    my rewritten notifydrop resolves this - altho it probably won't work between different tab groups:

    Code:
        ,notifyDrop: function(dd, e, data){
            this.tabpanel.arrow.hide();
            var tabs = this.tabpanel.items;
    
            var last = tabs.length;
            var eventPosX = e.getPageX();
    
            var newPos = last;
    //        dd.dropEl.position = last * 2 + 1; // default: 'behind the rest'
    
            var dragLeft=true;
    
            for(var i = 0; i < last-1; i++){            
                var tab = tabs.itemAt(i);
    
                if( dd.dropEl.id==tab.id ){
                    dragLeft=false;
                }
                
                // Is this tab target of the drop operation?
                var tabEl = tab.ds.dropElHeader;
                // Getting the absolute X coordinate of the tab
                var tabLeft = tabEl.getX();
                // Get the middle of the tab
                var tabMiddle = tabLeft + tabEl.dom.clientWidth / 2;
                
                var tabRight = tabLeft + tabEl.dom.clientWidth*1.5;
                
                if( dragLeft ){
                    if(eventPosX <= tabMiddle){
                        break;
                    }
                }else{
                    if(eventPosX <= tabRight){
                        break;
                    }
                }
            }
            
            //reset positions just in case new tabs were added
            tabs.each(function(tab, index){
    //            alert(tab.title);
                tab.position = (index + 1) * 2;
            });
    
            // Insert the tab element at the new position
    //        dd.proxy.hide();
            if( i+1==last ){
                dd.el.dom.parentNode.insertBefore(dd.el.dom, dd.el.dom.parentNode.childNodes[i].nextSibling);
                
                dd.dropEl.position=(last*2)+1;
            }else{
                if( dragLeft ){
                    dd.el.dom.parentNode.insertBefore(dd.el.dom, dd.el.dom.parentNode.childNodes[i]);
                
                    dd.dropEl.position=((i)*2)+1;
                }else{
                    dd.el.dom.parentNode.insertBefore(dd.el.dom, dd.el.dom.parentNode.childNodes[i+1]);
                
                    dd.dropEl.position=((i+1)*2)+1;
                }
            }
            
            // Sort tabs by their actual position
            tabs.sort('ASC', function(a, b){
                return a.position - b.position;
            });
            // Adjust tab position values
            tabs.each(function(tab, index){
                tab.position = (index + 1) * 2;
            });
            
            //var dropEl = dd.dropEl.ownerCt.remove(dd.dropEl, false);
            //this.tabpanel.insert(i, dropEl);
            //this.tabpanel.activate(dropEl);
    
            return true;
        }