Search code examples
extjsextjs4

ExtJS 4 Two collapsible panels with splitter


I have a container with two panels as it items. I want both panels to be collapsible (at the same time no one or only one panel can be collapsed) and if any panel is collapsed other panel should take all remaining space. Also I want to be able to resize both panels with Ext.resizer.Splitter.

I have tried different combinations of (h/v)box / border layout, but none of them works correctly.

It seems like Ext.layout.container.Accordion is what I need but, as I can see, it doest work with Ext.resizer.Splitter out of the box.

Check this fiddle

Also, I want to be able to collapse both panels with single Ext.resizer.Splitter, but as I can see its not available out of the box and I have to override it. Am I right?

I'm using ExtJS version 4.2.1.


Solution

  • Does this help?

    Ext.application({
    name: 'Fiddle',
    
    launch: function () {
    
        Ext.create('Ext.Container', {
            height: 500,
            renderTo: Ext.getBody(),
            width: 500,
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
    
            items: [{
                xtype: 'panel',
                reference: 'panel1',
                title: 'Top panel',
                collapsible: true,  // To allow collapse
                flex: 1,
                bodyStyle: 'background: #dadada',
                listeners: {
                    collapse: function(){
                        this.up().down("[reference='panel2']").expand();
                    }
                }
            }, 
            {
                xtype: 'splitter' 
            },
            {
                xtype: 'panel',
                reference: 'panel2',
                title: 'Bottom panel',
                collapsible: true,  // To allow collapse
                flex: 1,
                bodyStyle: 'background: #999',
                listeners: {
                    collapse: function(){
                        this.up().down("[reference='panel1']").expand();
                    }
                }
            }]
    
        });
    }});
    

    Here vbox is used to set the panels vertically (the flex property tells them how much space to take from each other). The collapsible property set to true makes them collapsible. Then each panel has an event that expands the sibling panel when the target panel collapses. With this, at least one panel is always expanded and you can resize them with the splitter!


    Edit 1:

    The previous code sample isn't generic and does not react as expected if we set panels with animCollapse: false. A generic solution would look like the following:

    // Our extended container
    Ext.define('MyVboxContainer', {
        extend: 'Ext.Container',
    
        layout: {
            type: 'vbox',
            align: 'stretch'
        },
    
        // We do the setup on initiating the component
        initComponent: function () {
            var me = this;
            var panelCount = 0;
    
            // For each child component
            this.items.forEach(function (comp) {
    
                // If the component is a panel
                if (comp.xtype === 'panel') {
    
                    // We add an unique ref
                    comp.reference = 'panel' + panelCount;
    
                    // Increment the total number of panels
                    panelCount++
    
                    // And listeners for beforecollapse, collapse and expand
                    comp.listeners = {
                        // On collpase, we track the last collapsed panel
                        'collapse': function () {
                            me.closedCount++;
    
                            me.lastClosed = this.reference;
                        },
    
                        // On expand we decrement the total number of panels collapsed
                        'expand': function () {
                            me.closedCount--;
                        },
    
                        // If this is the last panel being collapsed,
                        // we expand the previous collapsed panel
                        // Note: this cannot be done on the expand event
                        // if the panel has animCollapse: false
                        'beforecollapse': function () {
                            if (me.closedCount + 1 == me.totalPanels) {
                                me.down("[reference='" + me.lastClosed + "']").expand();
                            }
                        }
                    };
                }
            });
    
            this.totalPanels = panelCount; // total number of panels
            this.lastClosed = null; // Last collapsed panel
            this.closedCount = 0; // How many panels we have closed
    
            console.log("Total panels are: " + this.totalPanels)
    
            this.callParent();
        }
    });
    
    Ext.application({
        name: 'Fiddle',
    
        launch: function () {
    
            Ext.create('MyVboxContainer', {
                height: 500,
                width: 500,
                renderTo: Ext.getBody(),
    
                items: [{
                    xtype: 'panel',
                    animCollapse: false,
                    title: 'Top panel',
                    collapsible: true, // To allow collapse
                    flex: 1,
                    bodyStyle: 'background: #dadada'
                }, {
                    xtype: 'splitter'
                }, {
                    xtype: 'panel',
                    title: 'Middle panel',
                    animCollapse: false,
                    collapsible: true, // To allow collapse
                    flex: 1,
                    bodyStyle: 'background: #999'
                }, {
                    xtype: 'splitter'
                }, {
                    xtype: 'panel',
                    title: 'Bottom panel',
                    animCollapse: false,
                    collapsible: true, // To allow collapse
                    flex: 1,
                    bodyStyle: 'background: #999'
                }]
            });
        }
    });
    

    Here we create an extended container that uses vbox layout to set the panels vertically. On initComponent, this container setups the existing children panels to always keep the penultimate collapsed panel expanded. You should modify the algorithm to suit your needs.

    Sidenote: it isn't ideal to set variables on the view container. These variables should be moved into a controller.