PDA

View Full Version : Ext.ux.panel.DDTabPanel



Matti
18 Dec 2008, 6:20 AM
Ext.ux.panel.DDTabPanel extends Ext.TabPanel to support drag and drop operations to re-order the tabs.

This extension is the result of the work done in the topic "Draggable Panel in a TabPanel (http://extjs.com/forum/showthread.php?t=23264)". When I started using the code in that thread, I found that clicking a tab to activate it didn't work any longer, you had to start dragging for it to activate. After some investigation, this was fixed by adding an onMouseDown event to activate the tab. As suggested by mabello (http://extjs.com/forum/member.php?u=15518), I decided to publish this great piece of work by giving it its own topic and by publishing it on the UX Repository.

This extension is based on the code of thommy (http://extjs.com/forum/member.php?u=22731) and rizjoj (http://extjs.com/forum/member.php?u=37284) and was then fixed, polished and published by myself.

Demo: http://extjs-ux.org/repo/authors/Matti/trunk/Ext/ux/panel/DDTabPanel/demo.html
JS File: http://extjs-ux.org/repo/authors/Matti/trunk/Ext/ux/panel/DDTabPanel.js
CSS: http://extjs-ux.org/repo/authors/Matti/trunk/Ext/ux/panel/DDTabPanel.css
UX Repository: http://extjs-ux.org/docs/index.html?class=Ext.ux.panel.DDTabPanel

Okay, it's in the repository now. Some small adjustments to the documentation have been done which will be reflected in the repository in approximately an hour (the interval between two repository updates).

This thread is open for suggestions, questions, bug reports and appreciations! :)

Known bugs:
None, yay!Limitations:
The tab panel can't be made stateful.
It'd be very hard to do this as the default tab panel itself doesn't seem to support this either. Seen from a developer's point, it would be very hard too as some tabs are loaded dynamically through AJAX requests and such, how would one save those in a cookie?To-do's:
Allow tabs to be dropped on another tab panel by joining the drag and drop sources and targets.Changelog:
1.0.7 (Jan 16, 2009)
Implemented some more code optimizations from mystix (http://extjs.com/forum/showthread.php?p=272047#post272047).
1.0.6 (Jan 10, 2009)
Implemented mystix's suggestions (http://extjs.com/forum/showthread.php?p=268999#post268999).
1.0.5 (Dec 31, 2008)
Tabs with an iconCls now have a left padding on the ghost text.
1.0.4 (Dec 19, 2008)
Fixed bug with west region and code is much cleaner now. :)
1.0.3 (Dec 19, 2008)
Fixed activeTab configuration not working.
Added configuration option and method for tabs to enable/disable drag and drop on a per-tab basis.
1.0.2 (Dec 18, 2008)
Fixed conflict between multiple DDTabPanels.
1.0.1 (Dec 18, 2008)
Initial release.

mabello
18 Dec 2008, 6:49 AM
Matti,
Thanks for doing that.
You made an excellent job.
Keep up the good work!

galdaka
18 Dec 2008, 7:53 AM
Excellent work!!

Thanks for share,

gthe
18 Dec 2008, 8:04 AM
Thanks!
What about statefull panel order ?

Matti
18 Dec 2008, 9:27 AM
Thanks!
What about statefull panel order ?
Very good remark.
It seems like this doesn't work properly. I also found that multiple DDTabPanels seem to interfere with each other when dragging a tab. Further investigation will follow in how to solve these problems.

mystix
18 Dec 2008, 9:45 AM
+10. great job. =D>

p.s. a draggable option on each "tab" would be good.

Matti
18 Dec 2008, 11:02 AM
An update has been sent to the SVN (version 1.0.2) which should fix the conflict between multiple DDTabPanels. This was caused by the class using the same drop target identifier for all panels and the helper class selecting the wrong drop header. Well, that's what you get when you try to get the most out of a quickly written extension I guess. Maintaining this thing sounds a lot more creepy now... :P But I can handle it. ;)

+10. great job. =D>

p.s. a draggable option on each "tab" would be good.
Thank you. :)
That is a great idea indeed, I'll certainly have a look at it tomorrow.

galdaka
18 Dec 2008, 2:19 PM
Hi,

What about this extension: http://extjs.com/forum/showthread.php?t=32485

Here is demo: http://www.polomuseale.firenze.it/ext/test/test.html#

I prefer ghost dragging ;)


Greetings,

mholyszko
22 Dec 2008, 3:18 AM
Hello,

Thank you very much for this extension!

There is, unfortunately, a problem with tabs dragging when 'west' region is set. Something is wrong with calculating X offset where to put a dragged tab. Please try adding this to the first window items in the demo file and try it yourself:

{
region: 'west',
width: 200,
html: 'test'
},Moreover, I have the issue with drag "tip" rendering when tab has an icon set by iconCls property (see attached screenshot).

I would be very grateful if you could fix these issues.

moegal
22 Dec 2008, 4:08 AM
Doesn't seem to work in IE. This is a great extension though.

Marty

arthurakay
22 Dec 2008, 2:29 PM
I noticed that the demo page only worked if I enabled debugging in Firebug. I was convinced the page wasn't working until I started to write this comment and went back to look for an error.

That being said, I'm excited by this extension and I'm going to try it out.

arthurakay
22 Dec 2008, 3:24 PM
I'm actually getting some funkiness with the DD tabs... I'm going to post my code in hopes that someone can help me figure this out.

The problem that I'm seeing is that the tabs don't drop where the arrow is pointing. If I have fewer than 5 tabs open, I can only drag/drop tabs to the left... and even then the tab gets dropped one or two tabs further to the left of the arrow. Going to the right (when I have 5 or more tabs open) is just as frustrating.



Ext.onReady(function(){
/* ************************************
Default Tabs
************************************ */
var homeTab = new Ext.Panel({
allowDrag: false,
cls: 'container',
title: 'Home',
closable: false,
autoScroll: true
});

/* ************************************
Tab Panel Area
************************************ */
tabPanelRegion = new Ext.ux.panel.DDTabPanel({ //or Ext.TabPanel
activeTab: 0,
deferredRender: false,
enableTabScroll: true,
margins: '5 5 5 0',
minTabWidth: 150,
region: 'center',
resizeTabs: true,
stateful: true,
tabMargin: 5,
plugins: new Ext.ux.TabCloseMenu(),
items: [ homeTab ]
});

/* ************************************
Create the Page
************************************ */
var viewport = new Ext.Viewport({
layout: 'border',
items:
[
westRegion, //I'm not including the code for this
tabPanelRegion
]
});

});//end onReady()


I add new tabs via this code:


//add new tab to tab panel
var newTab = tabPanelRegion.add(new Ext.Panel(
{
cls: 'container',
title: tabTitle,
closable: true,
autoScroll: true
})
);

//focus on the new tab
tabPanelRegion.setActiveTab(newTab.getId());

//load the new tab's content
newTab.load({
url: urlLocation,
text: 'Loading data...',
timeout: 30,
scripts: true
});


What am I doing wrong? I'm wondering if I'm just missing some silly (yet important) config setting...

mholyszko
23 Dec 2008, 2:03 AM
(...) What am I doing wrong? I'm wondering if I'm just missing some silly (yet important) config setting...

The problem is caused by items in west region. I have the same problem - see my post above.

arthurakay
23 Dec 2008, 6:18 AM
Interesting... any idea if a fix or update is planned?

marcing
23 Dec 2008, 6:22 AM
Hi!
Firstly thanks for this wonderful extension!, Secondly...I'm having the same issue as mentioned above - tab drops in wrong place when west region is enabled. I hope You fix it soon, thanks :))

ifnot
25 Dec 2008, 7:52 AM
My code is ugly,but it seems work fine.:)
[code]
Ext.namespace('Ext.ux.panel');

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,

// Overwritten: assign the drag and drop group id
/** @private */
initComponent: function(){
Ext.ux.panel.DDTabPanel.superclass.initComponent.call(this);
this.ddGroupId = 'dd-tabpanel-group-' Ext.ux.panel.DDTabPanel.superclass.getId.call(this);
},

// Overwritten: declare the tab panel as a drop target
/** @private */
initEvents: function(){
Ext.ux.panel.DDTabPanel.superclass.initEvents.call(this);
// Create a drop target for this tab panel
var tabsDDGroup = this.ddGroupId;
this.dd = new Ext.ux.panel.DDTabPanel.DropTarget(this, {
ddGroup: tabsDDGroup
});
// Create a drop arrow indicator
this.arrow = Ext.DomHelper.append(
Ext.getBody(),
'

Matti
27 Dec 2008, 2:42 AM
Woah! I didn't expect so many replies... well, let me try to keep up! :)

Yes, the demo page only worked with Firebug. Seems like I missed a debugging line (console.log calls), my mistake. Fixed now. :D

As for the problem with the west region: that's very odd indeed. I use the DDTabPanel in a project and it functions properly, with a west region. However, I have the tab panel inside another panel in my layout, maybe that helps?

{
region: "center",
layout: "fit",
border: false,
autoHeight: true,
items: new Ext.ux.panel.DDTabPanel({
/* ... */
})
}The code suggested by ifnot doesn't look too great either, with so many try..catch blocks and dirty tricks. I believe there has to be a better solution, although I'm afraid you'll have to fit your tab panels in another panel for the moment...

If anyone could help finding a proper solution for this "west region bug", please don't hesitate to share! ;)

Matti
28 Dec 2008, 9:25 AM
Good news! :D I managed to fix the west region bug!

For some reason, the original authors used two different approaches to get the coordinates of the tab elements: one by using DOM properties, the other by using Ext methods. The Ext version has proven to always give correct results (what did you expect? ;)) and after implementing this change, everything functioned flawlessly! :)

What can I say? Get the new version from the trunk, forget about encapsulating your precious tab panels in dirty containers and enjoy!

mholyszko
29 Dec 2008, 4:58 AM
Thank you very much, Matti, tabs d&d works correctly now!

Now if you could just look on the "tab with icon" issue, which I described in my post above: http://extjs.com/forum/showthread.php?p=265810#post265810

Once again I would like to thank you for this great extension and effort you put in making it even better.

arthurakay
29 Dec 2008, 6:51 AM
That latest fix appears to fix all of the problems I was seeing... great job! =D>

Matti
31 Dec 2008, 2:00 AM
Thank you very much, Matti, tabs d&d works correctly now!

Now if you could just look on the "tab with icon" issue, which I described in my post above: http://extjs.com/forum/showthread.php?p=265810#post265810

Once again I would like to thank you for this great extension and effort you put in making it even better.
The problem here is the ExtJS CSS. On line 505 in ext-all.css, you'll find:

.x-tree-node a span, .x-dd-drag-ghost a span {
text-decoration:none;
color:black;
padding:1px 3px 1px 2px;
}Now, the problem here is that .x-dd-drag-ghost a span has a CSS specificity (http://htmldog.com/guides/cssadvanced/specificity/) of 12 (1 class selector + 2 tag selectors). Therefore, if you declare your icon class like...

.icon-home {
background-image:url(/path/to/home.gif);
padding-left: 20px;
}...the left padding won't be applied since .icon-home has a specificity of only 10 (a single class selector).

So, in order to make the left padding applied to the drag and drop proxy, you'll need to make your selector have a specificity greater than 12. For my site, I use

.x-dd-drag-ghost a span.icon-home {
padding-left: 20px;
}which has a specificity of 22 (2 class selectors + 2 tag selectors) and therefore it can override the padding. You could also use !important, however that could cause trouble with older browsers.

Well, I don't know it's possible to do this through code. You could try to give the proxy a style attribute which always overrides the selector styles, but then you first need to know whether the dragged element has an iconCls... I think it's possible, I'll have a look at it...

EDIT: Okay, good news. I found a quite simple way to do this without extra CSS needed. I managed to detect the iconCls and to select the span in the drag proxy. Version 1.0.5 is now available through the SVN. :)

mholyszko
4 Jan 2009, 1:58 AM
Thank you very much, it works flawlessly now :)

Remy
4 Jan 2009, 8:59 AM
...the left padding won't be applied since .icon-home has a specificity of only 1 (a single class selector).

I know its not terribly important but in the interest of learning, the above should be specificity of 10 rather than 1. Just a typo but I hadn't even heard of this term until you pointed it out. Thanks.

Remy

mystix
4 Jan 2009, 7:10 PM
some minor observations:

the drop indicator shouldn't appear on the immediate left / right of the tab being moved (refer to Firefox's / Opera's tab dragging behaviour).
when enableTabScroll = true, dragging a tab to the extreme right of the viewport when tab scrollers are present results in some real funky behaviour. set scroll = false on the dragsource to prevent this
i.e.


initTab: function(tp, item) {
// ... ...

tab.ds = new Ext.dd.DragSource(id, {
ddGroup: tabsDDGroup,
dropEl: tab,
dropElHeader: Ext.get(id, true),
scroll: false
});

// ... [snip] ...
}

some event / dom cleanup is required (i suggest moving the stuff in initEvents() to afterRender() instead since there's no event-related stuff in DDTabPanel's initEvents() method):


Ext.override(Ext.ux.panel.DDTabPanel, {
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 dd-arrow-down-invisible"></div>',
true
);

// Create a drop target for this tab panel
this.dd = new Ext.ux.panel.DDTabPanel.DropTarget(this, {
ddGroup: this.ddGroupId
});
},

[s]// initEvents: function() { /* ... */ } // moved everything to afterRender()

// dragsource cleanup on removed tabs
onRemove: function(tp, item) {
Ext.destroy(item.ds.proxy, item.ds);

Ext.ux.panel.DDTabPanel.superclass.onRemove.call(this, tp, item);
},

// droptarget + arrow cleanup
onDestroy: function() {
Ext.destroy(this.dd, this.arrow);

Ext.ux.panel.DDTabPanel.superclass.onDestroy.call(this);
}
});

why incur the overhead of creating a new Ext.Element() when you already have a reference which is an Ext.Element?
e.g. in notifyDrop():


var tab = tabs.itemAt(i),
tabDom = tab.ds.dropElHeader.dom, // Is this tab target of the drop operation?
tabLeft = new Ext.Element(tabDom).getX(), // Getting the absolute X coordinate of the tab
tabRight = tabLeft + tabDom.clientWidth,
tabMiddle = tabLeft + tabDom.clientWidth / 2; // Get the middle of the tab

versus simply


var tab = tabs.itemAt(i),
tabEl = tab.ds.dropElHeader, // Is this tab target of the drop operation?
tabLeft = tabEl.getX(), // Getting the absolute X coordinate of the tab
tabRight = tabLeft + tabEl.dom.clientWidth,
tabMiddle = tabLeft + tabEl.dom.clientWidth / 2; // Get the middle of the tab

moving the call to applyStyle() from Ext.ux.panel.DDTabPanel.DropTarget.notifyOver() to Ext.ux.panel.DDTabPanel.initTab() improves drag proxy performance


// init the drag source after (!) rendering the tab
initTab: function(tab, index) {

// ...

tab.ds.onStartDrag = function() {
if (this.dropEl.iconCls) {
this.getProxy().getGhost().select(".x-tab-strip-text").applyStyles({
paddingLeft: "20px" // Add left padding if the tab has an iconCls
});
}
};

// ... [SNIP] ...


the Ext.ux.panel.DDTabPanel.DropTarget constructor can be simplified to


constructor: function(tabpanel, config) {
this.tabpanel = tabpanel;

[s]// var target = Ext.select('div.x-tab-panel-header', false, tabpanel.getEl().dom).elements[0];

// The drop target is the tab strip wrap
Ext.ux.panel.DDTabPanel.DropTarget.superclass.constructor.call(this, tabpanel.stripWrap, config);
},

which should also enable drag / drop on tabPosition:'bottom' tabpanels
in complex layouts, the following modified check in Ext.ux.panel.DDTabPanel.DropTarget.notifyOver() will improve drag proxy performance:


// ...

if (last < 2 || !e.within(this.getEl())) {
return 'x-dd-drop-nodrop';
}

// ... [SNIP]

the dd-arrow-down-invisible css class is unnecessary. simply calling show() / hide() on the arrow element will achieve the same effect. e.g.:


notifyDrop: function() {
// ... [SNIP]

[s]// this.tabpanel.arrow.addClass('dd-arrow-down-invisible');
this.tabpanel.arrow.hide();

// ...
}

notifyOver: function(dd, e, data){
// ... [SNIP]

[s]// larrow.removeClass('dd-arrow-down-invisible');
larrow.show();

// ... [SNIP]
}




great job! =D>

arthurakay
7 Jan 2009, 1:19 PM
I'm noticing that in Opera 9.63, the right-click menus on the tabs don't work. Opera's own right-click menus show (for opening new tabs, etc).

I didn't see that listed anywhere as a bug or problem, so I thought I'd point it out. So far, this is the only browser in which I see the issue.

mystix
7 Jan 2009, 5:49 PM
I'm noticing that in Opera 9.63, the right-click menus on the tabs don't work. Opera's own right-click menus show (for opening new tabs, etc).

I didn't see that listed anywhere as a bug or problem, so I thought I'd point it out. So far, this is the only browser in which I see the issue.

it's impossible to disable the built-in right-click context menu since Opera 8.x

Matti
10 Jan 2009, 9:42 AM
Thank you for the great suggestions and code improvements, mystix! :D I really appreciate it!

I implemented almost all of your suggestions because they really make the code simpler, better and faster. The only thing which I didn't change was your first suggestion:


the drop indicator shouldn't appear on the immediate left / right of the tab being moved (refer to Firefox's / Opera's tab dragging behaviour).
Actually, Firefox does show an arrow next to the dragged tab. IE7 does the same thing, however I don't know about Opera as I have yet to install it. In my opinion, this is a good thing: it's logical that when you drag the tab to the left of the tab at the right, it shows an arrow but doesn't move.
Or, to say it with a quote:

"That's not a bug, that's an unexpected feature!" ;)


Anyway, check the first post (http://extjs.com/forum/showthread.php?p=264712#post264712) to get version 1.0.6. :)

mystix
11 Jan 2009, 1:07 AM
found a few more spots in v1.0.6 where redundant code can be removed:


Ext.override(Ext.ux.panel.DDTabPanel.DropTarget, {
notifyOver: function(dd, e, data) {
this.tabpanel = dd.dropEl.ownerCt; // this.tabpanel is already set in the constructor

// .... ...

// Getting the absolute Y coordinate of the tabpanel
[s]var panelDom = new Ext.Element(this.el.dom);
var tabPanelTop = panelDom.getY();
var tabPanelTop = this.el.getY(); // this.el is known, so just get its Y-coordinate

// ... ...

[s]larrow.setTop(tabPanelTop + tp.arrowOffsetY);
larrow.setLeft(left + tp.arrowOffsetX);
larrow.show();
larrow.setTop(tabPanelTop + this.tabpanel.arrowOffsetY).setLeft(left + this.tabpanel.arrowOffsetX).show(); // chain function calls, just because we can =)

return 'x-dd-drop-ok';
},

notifyDrop: function(dd, e, data) {
this.tabpanel = dd.dropEl.ownerCt; // this.tabpanel is already set in the constructor

// ... [SNIP] ...
},

notifyOut: function(dd, e, data) {
this.tabpanel = dd.dropEl.ownerCt; // this.tabpanel is already set in the constructor
this.tabpanel.arrow.hide();
}
});


i also made the following changes to Ext.ux.panel.DDTabPanel:

minor refactor using Ext.apply (just thought it looks neater) in the Ext.ux.panel.DDTabPanel.initTab()
removed the beforeDragEnter() method -- it's not required for tab activation
activate tab in the onMouseDown() method by simply calling show() on this.dropEl



Ext.override(Ext.ux.panel.DDTabPanel, {
// 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;
var tabsDDGroup = this.ddGroupId;

// default: enable drag on all tabs
Ext.applyIf(tab, { allowDrag: true });

Ext.apply(tab, {
// Set the initial tab position
position: (index + 1) * 2, // 2, 4, 6, 8, ... (2n)

// 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 drag proxy ghost element
onStartDrag: function() {
if (this.dropEl.iconCls) {
this.getProxy().getGhost().select(".x-tab-strip-text").applyStyles({
paddingLeft: "20px" // Add left padding if the tab has an iconCls
});
}
},

// Activate this tab before starting the drag action // removed redundant onMouseDown() method
beforeDragEnter: function(target, event, id) {
target.tabpanel.activate(this.dropEl);
this.dropEl.show();
},

// Activate this tab on mouse down
// (Fixed bug which prevents a tab from being activated by clicking it)
onMouseDown: function(event) {
if (!this.dropEl.isVisible()) {
this.dropEl.show(); // simply call the tab's show() method to activate it
}
}
}),

// Method to enable dragging
enableDrag: function() {
this.allowDrag = true;
return this.ds.unlock();
},

// Method to disable dragging
disableDrag: function() {
this.allowDrag = false;
return this.ds.lock();
}
});

// Initial dragging state
if (tab.allowDrag) {
tab.enableDrag();
} else {
tab.disableDrag();
}
}
});

Scorpie
12 Jan 2009, 1:02 AM
Wow, nice work mystix!

Mjollnir26
29 Jan 2009, 9:53 AM
Great thing, will help to make my App even sexier\:D/
Thanks very much!

Mjollnir26
29 Jan 2009, 10:01 AM
Just a small notice to anyone using TabPanelItems with an 'iconCls'
Set your background-repeat Property to 'no-repeat' or the Drag Indicator will look funky.

wm003
1 Feb 2009, 11:45 PM
Great stuff!=D>Thanks for sharing

vtswingkid
6 Feb 2009, 6:25 PM
Nice work... I made a few modifications so that I could drag tabs between multiple tabpanels...ddGroupId must be the same between all of them tabpanels.



/*global Ext*/
Ext.namespace('Ext.ux.panel');
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
});
},
// 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, {
// Set the initial tab position
position: (index + 1) * 2, // 2, 4, 6, 8, ... (2n)
// 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){
this.getProxy().getGhost().select(".x-tab-strip-text").applyStyles({
paddingLeft: "20px"
});
}
},
// Activate this tab on mouse down
// (Fixes bug which prevents a tab from being activated by clicking it)
onMouseDown: function(event){
if(!this.dropEl.isVisible()){
this.dropEl.show();
}
}
}),
// Method to enable dragging
enableDrag: function(){
this.allowDrag = true;
return this.ds.unlock();
},
// Method to disable dragging
disableDrag: function(){
this.allowDrag = false;
return this.ds.lock();
}
});
// Initial dragging state
if(tab.allowDrag){
tab.enableDrag();
}else{
tab.disableDrag();
}
},

// DragSource cleanup on removed tabs
/** @private */
onRemove: function(tp, item){
Ext.destroy(item.ds.proxy, item.ds);
Ext.ux.panel.DDTabPanel.superclass.onRemove.call(this, tp, item);
},
// 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(/*last < 2 ||*/ !e.within(this.getEl())){
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;
var eventPosX = e.getPageX();
for(var i = 0; i < last; 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){
left = tabLeft;
break;
}
}
if(typeof left == 'undefined'){
var lastTab = tabs.itemAt(last - 1);
if (lastTab) {
var dom = lastTab.ds.dropElHeader.dom;
left = (new Ext.Element(dom).getX() + dom.clientWidth) + 3;
}
}
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();
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 i;
for(i = 0; i < last; 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;
}
// Insert the tab element at the new position
dd.proxy.hide();
var dropEl = dd.dropEl.ownerCt.remove(dd.dropEl, false);
this.tabpanel.insert(i, dropEl);
this.tabpanel.activate(dropEl);
return true;
},
notifyOut: function(dd, e, data){
this.tabpanel.arrow.hide();
}
});
Ext.reg('ddtabpanel', Ext.ux.panel.DDTabPanel);

vtswingkid
7 Feb 2009, 5:39 AM
Might also consider merging work with dockpanel:

http://extjs.com/forum/showthread.php?t=32485&highlight=dock

wm003
15 Feb 2009, 9:11 AM
Might also consider merging work with dockpanel:

http://extjs.com/forum/showthread.php?t=32485&highlight=dock

Yeah, that would be nice to have it as a plugin so any kind of panel can be dragged to eachother :)

adam.jimenez
19 Feb 2009, 3:39 PM
I have a tabpanel with dynamically added tabs.

I'm finding that after a few tabs have been added and your reorder them sometimes the tab you have dragged disappears!

I also get this error:

Error: m.childNodes[1] is undefined
Source File: /ext-all.js
Line: 9

I'm using FF3.

Mjollnir26
20 Feb 2009, 12:34 AM
Do you have the latest Version? I got FF 3 too and add my TabPages dynamically also but see none of the behaviour you describe.

adam.jimenez
20 Feb 2009, 1:51 AM
Do you have the latest Version? I got FF 3 too and add my TabPages dynamically also but see none of the behaviour you describe.


yep, have the latest version of everything.

set up a test case here based on the ext demo.

http://lib.shiftcreate.com/dev/ddtabs.php

tabs aren't disappearing but am still getting this error sometimes when i drag and drop new tabs:
Error: m.childNodes[1] is undefined
Source File: /ext-all.js
Line: 9

adam.jimenez
20 Feb 2009, 5:26 PM
i seem to have fixed it by changing this:

line: 220:

for(var i = 0; i < last-1; i++){

adam.jimenez
20 Feb 2009, 5:29 PM
there also seems to be another bug which you can recreate on the demo.

if you drag tab 1 to the right. the arrow points between tab 1 and tab 2.
if you release it the tabs should stay where they are but instead they switch positions.

dawesi
23 Feb 2009, 5:20 AM
Great extension... people also need to know Adobe own a patent for some of this this functionality (grouping movable tabs), so be careful using this in a potential IDE.

this is great.

adbox
3 Mar 2009, 3:47 PM
there also seems to be another bug which you can recreate on the demo.

if you drag tab 1 to the right. the arrow points between tab 1 and tab 2.
if you release it the tabs should stay where they are but instead they switch positions.

thanks creators, it's installed and running!
-- on the other side the bug mentioned above is still around today. has someone figured out of quick fix?

adam.jimenez
4 Mar 2009, 3:43 AM
thanks creators, it's installed and running!
-- on the other side the bug mentioned above is still around today. has someone figured out of quick fix?


I have a fix for this:

http://lib.shiftcreate.com/dev/ddtabs.php

I'm sure the code could be more concise but it works!

adbox
4 Mar 2009, 7:03 AM
thank you very much adam.jimenez. it fixed it.

adam.jimenez
4 Mar 2009, 9:53 AM
I was having more issues with DDtabs!

DDtabs works by removing the tab that's being moved and then reinserting it.

This was causing me problems because I have a listener that prompts before a tab is closed. Another issue is that my app depends on the tab having the same id; which was changing when it gets destroyed and recreated.

To resolve this issue I've modified DDtabs so that it just moves tabs instead of destroying them.

code is here:
http://lib.shiftcreate.com/dev/tabs/ddtabs.php

vtswingkid
5 Mar 2009, 5:41 PM
adam: this does not work when dragging from one panel to another.

matti: funny things happen when the last tab is dragged to another panel and then back again. It seems like the body gets left behind or something.

this seems to take care of it...apparently the activetab wasn't getting cleared.



onRemove:function(tp, item){
Ext.destroy(item.ds.proxy, item.ds);



Ext.ux.panel.DDTabPanel.superclass.onRemove.call(this, tp, item);
if(this.items.getCount()==0)this.activeTab=null;
}

adam.jimenez
6 Mar 2009, 2:08 AM
adam: this does not work when dragging from one panel to another.

that's not a requirement of my ap. in order for that to work you would have to delete the tab and then recreate it - which would cause the issues I outlined before.

tobiu
25 Apr 2009, 6:19 AM
hi Matti,

nice work so far.
i found some edges, we can take a look at:



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.isVisible() && !this.dropEl.disabled){
this.dropEl.show();
}
}

...

notifyDrop: function(dd, e, data){
...
var dropEl = dd.dropEl.ownerCt.remove(dd.dropEl, false);
this.tabpanel.insert(i, dropEl);
if(!dropEl.disabled)this.tabpanel.activate(dropEl);

return true;
}


ok, lets go through the points:

the green part is optional, i dont think many users need it.
it's just nice, if you put html around your titles, which is only a workaround
(like title: '<div id="myTab">TabTitle</div>')

the red parts are the important ones:



el.addClass('x-panel-inline-icon');


if you have icons defined in your css, that do not have "no repeat", not setting this class makes trouble. since the tabHeader has it, the proxy should also.




onMouseUp


if you are in tab 1 (active tab) and want to swap the position of tab 3 and tab 4,
activating tab 3 is the last thing i want (if there is a huge grid inside for example).
with this tradeoff, you have to wait a tick longer when not dd'ing, but i prefer it this way.



if(!this.dropEl.isVisible() && !this.dropEl.disabled){
this.dropEl.show();


showing disabled tabs is a "must not".



if(!dropEl.disabled)this.tabpanel.activate(dropEl);


activating disabled tabs should also not come to happen.

kind regards, tobiu

tobiu
25 Apr 2009, 9:26 AM
[edit]: latest version 27.4.09

ok, i found some major bugs, so i decided to debug and rework the last version a bit more.
here is the 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
* @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 1.0.7 (Jan 16, 2009)
*/
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
});
},

// 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 down
// (Fixes bug which prevents a tab from being activated by clicking it)
onMouseUp: function(event){
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();
}
},

// DragSource cleanup on removed tabs
/** @private */
onRemove: function(tp, item){
Ext.destroy(item.ds.proxy, item.ds);
Ext.ux.panel.DDTabPanel.superclass.onRemove.call(this, tp, item);
},

// 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;
}

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--;
}

var dropEl = dd.dropEl.ownerCt.remove(dd.dropEl, false);
this.tabpanel.insert(i, dropEl);
if(!dropEl.disabled)this.tabpanel.activate(dropEl);

return true;
},

notifyOut: function(dd, e, data){
this.tabpanel.arrow.hide();
}
});

Ext.reg('ddtabpanel', Ext.ux.panel.DDTabPanel);


comments on the changes in my next answer, beautification soon.

kind regards, tobiu

tobiu
25 Apr 2009, 9:52 AM
// if tab stays in the same tabPanel
if(dd.dropEl.ownerCt == this.tabpanel){
if(i > tabs.indexOf(dd.dropEl))i--;
}


is one of the keys of my update.
if a tab is moved within the same panel,
moving to the left works as intended.

moving to the right does the following:

tabPosition = 3;
targetPosition = 5;

removeTab(3);
index5 gets 4.
insert(5);

always one position to far to the right.



if(tab == dd.dropEl || prevTab == dd.dropEl){
this.tabpanel.arrow.hide();
return 'x-dd-drop-nodrop';
}




if(tab == dd.dropEl || tabs.itemAt(i-1) == dd.dropEl){
return false;
}


neither dropping a tab at the current position nor showing a drop-notification.
this means not showing a left or a right arrow.

if you have any questions, tell me ;)

kind regards, tobiu

tobiu
27 Apr 2009, 2:21 AM
i made a little mistake in the notifyOnOver-method.
its fixed in the version above.

i changed the part:



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';
}


otherwise the arrow-icon will never get displayed on the right side of the last item.

kind regards, tobiu

tobiu
27 Apr 2009, 5:40 AM
yay, i found a horrible nested bug:

change the following code:



// 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();
}


if the method enableTabDrag is called enableDrag,
you get serious problems, if you have gridPanels in your tabPanel.

the following happens in the gridView:



if(g.enableDragDrop || g.enableDrag){
this.dragZone = new Ext.grid.GridDragZone(g, {
ddGroup : g.ddGroup || 'GridDD'
});


so, our tab -> gridPanel g will enter the if-clause, since it has the method enableDrag().
this is like always setting



enableDragDrop : true


in the grid-config.

kind regards, tobiu

[edit]: updated the code some postings above

ttbgwt
6 Oct 2009, 6:47 AM
Where can I get the latest and greatest code? The version I see is 1.0.7 (Jan 16, 2009) on the first page of this post...

Also, The last tab in my app is fixed and show always remain in the far right position, so I dont want to be able to drag any tabs past it into the last (far right) position. How can I accomplish this? Thanks!

tobiu
12 Oct 2009, 12:01 AM
hi ttbgwt,

i am still using the version of #49,
http://www.extjs.com/forum/showthread.php?p=321685#post321685

kind regards, tobiu

mschwartz
10 Nov 2009, 10:39 AM
I tested this with Ext 3.0.0 and it works great.

It even works with enableTabScroll turned on and lots of tabs and scrolled.

mschwartz
10 Nov 2009, 11:21 AM
I tested this with Ext 3.0.0 and it works great.

It even works with enableTabScroll turned on and lots of tabs and scrolled.

I take it back.

Some of the tabs don't activate when clicked on. I will diagnose further.

mschwartz
10 Nov 2009, 11:30 AM
This is the code that makes it fail to activate tabs when clicking on them, and the commentd out lines fixes it (for me):



// Activate this tab on mouse down
// (Fixes bug which prevents a tab from being activated by clicking it)
onMouseUp: function(event){
// if(!this.dropEl.isVisible() && !this.dropEl.disabled){
this.dropEl.show();
// }
}

mschwartz
10 Nov 2009, 11:32 AM
And it doesn't work like this:



// Activate this tab on mouse down
// (Fixes bug which prevents a tab from being activated by clicking it)
onMouseUp: function(event){
if(!this.dropEl.isVisible()) { // && !this.dropEl.disabled){
this.dropEl.show();
}
}

tobiu
12 Nov 2009, 2:59 AM
hi mschwartz,

i am using this plugin with ext3.0-rc2, had no time to change to the final yet.
i am using tabScroll and the tabMenu with this together too.
could you describe in more detail, which tabs do not activate.

to explain your commented code:

// thats the price of dd, on click or mouseDown wont do for activation since it starts the dd
onMouseUp: function(event){

// do not activate a tab, that is already activated
!this.dropEl.isVisible()

// tabs with the config disabled (or dynamically disabled) may never be activated
!this.dropEl.disabled


kind regards,
tobiu

Xander75
12 Nov 2009, 5:55 AM
Hi,

Is it possible to move a Tab through code?

For example, if there are 5 tabs, move tab 5 to tab position 1 using code.

mschwartz
12 Nov 2009, 6:18 AM
hi mschwartz,

i am using this plugin with ext3.0-rc2, had no time to change to the final yet.
i am using tabScroll and the tabMenu with this together too.
could you describe in more detail, which tabs do not activate.

to explain your commented code:

// thats the price of dd, on click or mouseDown wont do for activation since it starts the dd
onMouseUp: function(event){

// do not activate a tab, that is already activated
!this.dropEl.isVisible()

// tabs with the config disabled (or dynamically disabled) may never be activated
!this.dropEl.disabled


kind regards,
tobiu

The 1st tab and 2nd tab for sure won't activate. When I add a 3rd and 4th, sometimes they won't activate, sometimes they will.

This is by clicking on the tabs...

The fix I posted does make it work.

tobiu
12 Nov 2009, 6:28 AM
@Xander75:

you don't need this plugin therefore.
you can access the MixedCollection of a TabPanel with TabPanel.items and then use the insert()-method described in the docs of Ext.util.MixedCollection.

@mschwartz:

if "!this.dropEl.disabled" causes trouble, you have to find out why those tabs are disabled. i will test it when i change to Ext3.0 stable, but it may take about 2 weeks.


kind regards,
tobiu

mschwartz
12 Nov 2009, 6:30 AM
@Xander75:

you don't need this plugin therefore.
you can access the MixedCollection of a TabPanel with TabPanel.items and then use the insert()-method described in the docs of Ext.util.MixedCollection.

@mschwartz:

if "!this.dropEl.disabled" causes trouble, you have to find out why those tabs are disabled. i will test it when i change to Ext3.0 stable, but it may take about 2 weeks.


kind regards,
tobiu

None of the tabs are disabled. I am using the tab menu plugin from the Ext examples, though.

Xander75
12 Nov 2009, 6:45 AM
@tobiu:

Thanks, can't believe I missed the insert() method in the API. I will look into that.

I have still found this extension of the TabPanel to be very useful for another part of my project.

simplessus
21 Dec 2009, 12:19 AM
Hello,

first of all: Great component, I love to use it.

I downloaded the new update 3.1 and it seems as if some modifications need to be done to use DDTabPanel with this update.

When closing a tab I receive a JS error and moving of the Tabs does not work:


item is undefined
Ext.destroy(item.ds.proxy, item.ds);
This comes from:


onRemove: function(tp, item)
Ext.destroy(item.ds.proxy, item.ds);
Ext.ux.panel.DDTabPanel.superclass.onRemove.call(this, tp, item);
},
Best regards,
Bjoern

tobiu
21 Dec 2009, 1:25 AM
hi simplessus,

i have not looked into ext-3.1 yet. as a workaround, try:



onRemove: function(tp, item)
if(item)Ext.destroy(item.ds.proxy, item.ds);
Ext.ux.panel.DDTabPanel.superclass.onRemove.call(this, tp, item);
}


but this wont help with the problem. in 3.0.3, the onRemove-method of the tabpanel has the interface onRemove: function(tabpanel this, tab item) containing the tp (scope) and the tab to remove. calling this method without an item to remove makes no sense, so i guess the interface has changed. so, you will have to look into the source of Ext.Tabpanel first.

kind regards,
tobiu

JamesC
31 Dec 2009, 3:58 AM
Fix for onRemove is as follows:



onRemove: function(item) {
Ext.destroy(item.ds.proxy, item.ds);
Ext.ux.panel.DDTabPanel.superclass.onRemove.call(this, item);
},


However when I drop a tab I now get an "el is undefined" error.

miti
21 Jan 2010, 4:13 PM
Fix for onRemove is as follows:



onRemove: function(item) {
Ext.destroy(item.ds.proxy, item.ds);
Ext.ux.panel.DDTabPanel.superclass.onRemove.call(this, item);
},


However when I drop a tab I now get an "el is undefined" error.

I can second this. Any solutions?

tobiu
22 Jan 2010, 1:23 AM
please take a look at my last posting. if you still have issues with that fix, let me know.

kind regards,
tobiu

tobiu
13 Feb 2010, 11:06 AM
i am just porting my app over to ext-3.1.1 and this plugin is no longer working there.
the problem is endDrag, which causes trouble with the new

Ext.Element.getStyle()

el is undefined


out = (v = el.style[prop]) ? v :
(cs = view.getComputedStyle(el, "")) ? cs[prop] : null;


so, i will start debugging this now. if someone else already did it, please let me know.

kind regards,
tobiu

tobiu
13 Feb 2010, 3:56 PM
the [3.1.1]-version is here:
http://www.extjs.com/forum/showthread.php?t=92033

i opened a new topic, since this is still in the 2.* ux folder ;)

kind regards,
tobiu

mjmonserrat
18 May 2010, 11:16 PM
Hi tobiu,

I'm having problem with tabs when I closed it. All closed tabs doesn't disappear. Instead it's items cleared out. It leaves a blank tab

george.neculai
12 Jan 2011, 2:37 AM
Hi people.

When can one download this extension from? The download sources on the first page seem unaccessible no more.

I really could use such a functionality in my app.

Thank you for the great work you are doing.