PDA

View Full Version : [FIXED][2.*,3.0] layout in collapsed Panels



Animal
27 Mar 2009, 5:53 AM
When Panels (And their subclass, Fieldset) collapse, they hide the bwrap Element using display: none

This means that any layout code performed in a Panel which is collapsed is prone to sizing errors.

Laying out of child items in a collapsed Panel must be deferred until such time as the Panel is expanded.

The following override is necessary:



Ext.override(Ext.Panel, {
doLayout: function() {
var a = arguments;

// If collapsed, then defer layout operation until the next time this Panel is expended.
if (this.collapsed) {
this.on('expand', function() {
this.doLayout.apply(this, a);
}, this, {single: true});
return;
}
Ext.Panel.superclass.doLayout.apply(this, a);
}
});

Condor
27 Mar 2009, 6:17 AM
This should not only be used in collapse/expand, but also in show/hide/render.

Here is my related feature request (http://extjs.com/forum/showthread.php?t=62178).

Animal
27 Mar 2009, 6:21 AM
Well spotted Condor. I do like your solution better because it uses the lifecycle Template Methods (onShow and afterExpand) instead of an event handler (and events can be suspended by the user).

I'll add a vote to your thread.

mjlecomte
27 Mar 2009, 6:32 AM
Here's some code to illustrate the problem.

Just comment out the override to see the problem. Nige's or Condor's override fixes this particular problem.

Also: if I run this code I get a horizontal scroller in the grid for some reason.



<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Fieldset - with Panel</title>

<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>

<style>
fieldset.x-panel-collapsed {
border-width: 1px 1px 0px 1px!important;
border-left-color: transparent!important;
border-right-color: transparent!important;
}
</style>



<script type="text/javascript">
Ext.BLANK_IMAGE_URL = "../../resources/images/default/s.gif";

Ext.override(Ext.Container, {
doLayout : function(shallow){
if(!this.isVisible() || this.collapsed){
this.deferLayout = this.deferLayout || !shallow;
return;
}
shallow = shallow && !this.deferLayout;
delete this.deferLayout;
if(this.rendered && this.layout){
this.layout.layout();
}
if(shallow !== false && this.items){
var cs = this.items.items;
for(var i = 0, len = cs.length; i < len; i++) {
var c = cs[i];
if(c.doLayout){
c.doLayout();
}
}
}
},
onShow : function(){
Ext.Container.superclass.onShow.apply(this, arguments);
if(this.deferLayout !== undefined){
this.doLayout(true);
}
}
});
Ext.override(Ext.Panel, {
afterExpand : function(){
this.collapsed = false;
this.afterEffect();
if(this.deferLayout !== undefined){
this.doLayout(true);
}
this.fireEvent('expand', this);
}
});

Ext.onReady(function(){

var height = 100

var panel = {
xtype: 'panel',
anchor: '100%',
title: 'Panel inside a fieldset',
frame: true,
height: height
};


var getGrid = function () {
var myData = [
['Fred Flintstone', 'Fred'],
['Barney Rubble', 'Barney']
];

var rt = Ext.data.Record.create([
{name: 'fullname'},
{name: 'first'}
]);

var ds = new Ext.data.ArrayStore({
fields: ['fullname', 'first'],
reader: new Ext.data.ArrayReader({id: 0}, rt)
});

ds.loadData(myData);

var colModel = new Ext.grid.ColumnModel([
{header: 'Full', id: 'expand'},
{header: 'First'}
]);

// create (instantiate) the Grid
var grid = {
xtype: 'grid',
anchor: '100%',
title: 'Grid inside a fieldset',
frame: true,
height: height,
store: ds, //the DataStore object to use (ds: is shorthand)
colModel: colModel, //the ColumnModel object to use (cm: is shorthand)
autoExpandColumn: 'expand'
};

return grid;
};

var createContainer = function(config, useGrid) {

var cfg = Ext.apply({
labelWidth: 75, // label settings here cascade unless overridden
frame:true,
width: 700,
//autoHeight: true,
renderTo: document.body,
layout:'column', // arrange items in columns
defaults: { // defaults applied to items (fieldsets) in column layout
xtype: 'fieldset',
autoHeight: true,
// border: false,
collapsible: true,
columnWidth: 0.5,
//layout: 'form',
bodyStyle:'padding:4px'
},
items: [{
// Fieldset in Column 1
title: 'Fieldset 1',
defaults: { // defaults applied to items (textfields) in column layout
anchor: '-20' // leave room for error icon
},
defaultType: 'textfield',
items :[
{fieldLabel: 'Field 1'},
{fieldLabel: 'Field 2'},
{fieldLabel: 'Field 3'},
{fieldLabel: 'Field 4'}
]
},{
// Fieldset in Column 2 - Panel inside
title: 'Show Panel',
collapsed: config.collapseState,
layout:'anchor',
bodyStyle:'padding:4px 0px 4px 4px',
items : [
useGrid ? getGrid() : panel
]
}]
}, config)

return new Ext.FormPanel(cfg);
}

var form1 = createContainer({
title: 'Form 1 - Panel NOT initially collapsed',
collapseState : false // fieldset NOT initially collapsed
});

var form2 = createContainer({
title: 'Form 2 - Panel IS initially collapsed',
collapseState : true // fieldset IS initially collapsed
});

var form3 = createContainer({
title: 'Form 3 - Grid Panel NOT initially collapsed',
collapseState : false // fieldset NOT initially collapsed
}, true);

var form4 = createContainer({
title: 'Form 4 - Grid Panel IS initially collapsed',
collapseState : true // fieldset IS initially collapsed
}, true);

});
</script>
</head>
<body></body>
</html>

mjlecomte
27 Mar 2009, 6:45 AM
This: http://extjs.com/forum/showthread.php?p=309418#post309418
version of Condor's works fine also.

Only the horizontal scroll of grid remains...

mjlecomte
27 Mar 2009, 6:55 AM
One more note to core team:

The above code includes a style override for the fieldset. This override is already part of the Ext3 base, but probably should be applied to Ext2 as well.

mjlecomte
27 Mar 2009, 7:22 AM
I noticed the grid horizontal scroller is a separate issue from the fieldset.

Looks like all of the grid examples in Ext 3 have a horizontal scroll issue (on FF3 and IE7 at least).

aconran
4 May 2009, 11:11 AM
Marking this as an open issue.

Animal
5 May 2009, 12:47 AM
Great.

But use Condor's solution - it's better.

mjlecomte
4 Jun 2009, 12:47 PM
As of this post, this was marked INFOREQ. Some code has been posted to deferLayout, etc. Not sure if that helps this thread or not. I'm going to change this to OPEN pending anyone verifying if this issue has been resolved.

evant
4 Jun 2009, 12:53 PM
Fairly sure this has been resolved, items in collapsed/hidden panels aren't laid out.

mjlecomte
15 Jun 2009, 9:49 AM
This appears to be "mostly" fixed. The height of the initially collapsed panels are taller than the initially shown counterparts.

In the code I tested with I note the following:

the height of the panel is set to 100
the overall of the panel that is initially not hidden is 100
the height of "x-panel-ml" that is initially hidden is 100 (so the overall height is larger)


Code I used to test against:



<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Fieldset - with Panel</title>

<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>

<style>
fieldset.x-panel-collapsed {
border-width: 1px 1px 0px 1px!important;
border-left-color: transparent!important;
border-right-color: transparent!important;
}
</style>



<script type="text/javascript">
Ext.BLANK_IMAGE_URL = "../../resources/images/default/s.gif";

Ext.onReady(function(){

var height = 100;

var panel = {
xtype: 'panel',
anchor: '100%',
title: 'Panel inside a fieldset',
frame: true,
height: height
};

var getGrid = function () {
var myData = [
['Fred Flintstone', 'Fred'],
['Barney Rubble', 'Barney']
];

var rt = Ext.data.Record.create([
{name: 'fullname'},
{name: 'first'}
]);

var ds = new Ext.data.ArrayStore({
fields: ['fullname', 'first'],
reader: new Ext.data.ArrayReader({id: 0}, rt)
});

ds.loadData(myData);

var colModel = new Ext.grid.ColumnModel([
{header: 'Full', id: 'expand'},
{header: 'First'}
]);

// create (instantiate) the Grid
var grid = {
xtype: 'grid',
anchor: '100%',
title: 'Grid inside a fieldset',
frame: true,
height: height,
store: ds, //the DataStore object to use (ds: is shorthand)
colModel: colModel, //the ColumnModel object to use (cm: is shorthand)
autoExpandColumn: 'expand'
};

return grid;
};

var createContainer = function(config, useGrid) {

var cfg = Ext.apply({
labelWidth: 75, // label settings here cascade unless overridden
frame:true,
width: 700,
//autoHeight: true,
renderTo: document.body,
layout:'column', // arrange items in columns
defaults: { // defaults applied to items (fieldsets) in column layout
xtype: 'fieldset',
autoHeight: true,
// border: false,
collapsible: true,
columnWidth: 0.5,
//layout: 'form',
bodyStyle:'padding:4px'
},
items: [{
// Fieldset in Column 1
title: 'Fieldset 1',
defaults: { // defaults applied to items (textfields) in column layout
anchor: '-20' // leave room for error icon
},
defaultType: 'textfield',
items :[
{fieldLabel: 'Field 1'},
{fieldLabel: 'Field 2'},
{fieldLabel: 'Field 3'},
{fieldLabel: 'Field 4'}
]
},{
// Fieldset in Column 2 - Panel inside
title: 'Show Panel',
collapsed: config.collapseState,
layout:'anchor',
bodyStyle:'padding:4px 0px 4px 4px',
items : [
useGrid ? getGrid() : panel
]
}]
}, config)

return new Ext.FormPanel(cfg);
}

var form1 = createContainer({
title: 'Form 1 - Panel NOT initially collapsed',
collapseState : false // fieldset NOT initially collapsed
});

var form2 = createContainer({
title: 'Form 2 - Panel IS initially collapsed',
collapseState : true // fieldset IS initially collapsed
});

var form3 = createContainer({
title: 'Form 3 - Grid Panel NOT initially collapsed',
collapseState : false // fieldset NOT initially collapsed
}, true);

var form4 = createContainer({
title: 'Form 4 - Grid Panel IS initially collapsed',
collapseState : true // fieldset IS initially collapsed
}, true);

});
</script>
</head>
<body></body>
</html>

evant
16 Jun 2009, 11:59 AM
A fix has been committed that solves your test case too. It was incorrectly forcing a layout.

mjlecomte
16 Jun 2009, 12:33 PM
A fix has been committed that solves your test case too. It was incorrectly forcing a layout.

Thank you sir, looks good against that posted test case. =D>

mjlecomte
16 Jun 2009, 1:53 PM
Fairly sure this has been resolved, items in collapsed/hidden panels aren't laid out.

The problem I see is that the mixed collection of items won't be created either. So if someone tries to submit or validate a form that has hidden/collapsed fields those may not have a valid mixed collection created yet with an each() method. Seems forms need to potentially force hidden items to lay out, or refactor when the creation of the mixed collection occurs.