mitchellsimoens / Ext.layout.AccordionLayout

An accordion style layout for Sencha Touch
http://www.simoens.org/Sencha-Projects/demos/
25 stars 9 forks source link

AccordionLayout only works with items with html #1

Open erikkaplun opened 13 years ago

erikkaplun commented 13 years ago

Currently it seems that this accordion layout implementation only works with items that have the 'html' property, not 'items'.

This does not work:

new Ext.Panel({
    id: 'jobs',
    fullscreen: true,
    layout: 'accordion',

    items: [
        {title: 'foo', items: {xtype: 'button', text: 'foo'}},
        {title: 'bar', items: {xtype: 'button', text: 'bar'}},
    ],
});

However, the following works:

new Ext.Panel({
    id: 'jobs',
    fullscreen: true,
    layout: 'accordion',

    items: [
        {title: 'foo', html: 'foo'},
        {title: 'bar', html: 'bar'},
    ],
});

And of course, when I remove layout: 'accordion' from the first example, the buttons are displayed as expected.

ghost commented 13 years ago

@eallik, I am seeing this behavior as well. I believe the issue may be related to the lazy loading when using xtype as the controls are not rendered when the layout is drawing the accordion item panels...and these heights need to be known. Try instantiating new Ext.Button classes above the layout panel and insert the button object reference instead. Let me know if that works.

Also, what I am now doing to make this work a little better is to add a class to the control to render in with a position of absolute and a z-index of -99 or something. This will let the full control draw itself which will let all the heights of the individual containers be known, then immediately after the layout is drawn I call the following on the accordion component container:

cmp.removeCls('accordion_container_hidden').addCls('accordion_container');

I have also added the following method to the accordion.js file to collapse all accordion items to their headers, and I call this as the last line in the accordion's onLayout() method:

collapseAll: function () {
    var me = this,
        items = me.getLayoutItems()

    Ext.each(items, function (item) {
        me.collapseItem(item);
    });
}

Mitchell is doing a great job with this layout...hopefully he'll be able to come back to it soon and incorporate some of these thoughts.

erikkaplun commented 13 years ago

This does not work; the items of the root panel are rendered empty:

new Ext.Panel({
    layout: 'accordion',
    items: [
        new Ext.Panel({
            title: 'Foo',
            items: [
                new Ext.Panel({html: 'Foo'}),
            ],
        }),
        new Ext.Panel({
            title: 'Bar',
            items: [
                new Ext.Panel({html: 'Bar'}),
            ],
        }),
    ],
});

This works and items contain 'Foo' and 'Bar' accordingly:

new Ext.Panel({
    layout: 'accordion',
    items: [
        new Ext.Panel({
            title: 'Foo',
            html: 'Foo'
        }),
        new Ext.Panel({
            title: 'Bar',
            html: 'Bar'
        })
    ]
});
erikkaplun commented 13 years ago

Can you provide an example of how you are using the accordion_container_hidden and accordion_container class trick?

erikkaplun commented 13 years ago

Also, I did:

.x-accordion-item { height: 200px; }

which does set the height, but does not fix the original issue — the items are still rendered empty, just with a height of 200 pixels.

ghost commented 13 years ago

There may be an issue with the layout not properly rendering the Child controls...honestly I haven't had time to look to far into it. I tried to render a form in a panel once and saw that the container was render but not the textfields...though I think I recall seeing them in the html stream.

My CSS is as follows: .accordion_container_hidden { background-color: #ffffff; padding: 4px;
z-index: -99; position: absolute; }

.accordion_container { background-color: #ffffff; padding: 4px;
}

I commented out a few line too in the code and I have adjusted it slightly to allow multiple items to be opened at once, rather than auto-closing the others when one is opened. There is still one problem that I need to go back and fix and that I am not using an activeItem now so that causes an issue when you open one header, then another, then come back to close the first one...as its not the active item so you have to click it twice to close. Should be simple fix.

Here is my full Accordion.js code: Ext.ns("Ext.layout"); Ext.layout.AccordionLayout = Ext.extend(Ext.layout.ContainerLayout, { /* * @cfg {Number} activeItem * The active item to start active. * Default: undefined / / * @cfg {Number} minHeight * Minimum height of expanded component. * Default: undefined / / * @cfg {Number} maxHeight * Masimum height of expanded component. * Default: undefined / / * @cfg {String} itemCls * Default CSS class to be added to each item. * Default: 'x-accordion-item' / itemCls: "x-accordion-item", / * @cfg {String} headerCls * Default CSS class to be added to each item's header. * Default: 'x-accordion-header' / headerCls: "x-accordion-header", / * @cfg {String} arrowCls * Default CSS class to be added to each item's arrow. * Default: 'x-accordion-arrow' / arrowCls: "x-accordion-arrow", / * @cfg {String} targetCls * Default CSS class to be added to target el. * Default: 'x-layout-accordion' / targetCls: "x-layout-accordion", / * @cfg {Boolean} allowCollapse * Allow all items to be collapsed at the same time. * Default: false / allowCollapse: true, / * @cfg {Boolean} allowCollapse * Allow all items to be collapsed at the same time. * Default: false / forceAutoCollapse: true, / * @cfg {String} easing * Easing that animations will have. * Default: 'ease-in' / easing: "ease-in", / * @cfg {Number} animDuration * Number of milliseconds the animations will take. * Default: 300 / animDuration: 300, / * @cfg {Boolean} fill * Whether or not to have items fill all available space. * Will take into consideration the minHeight and maxHeight * Default: false / fill: false, / * @private / type: "accordion", / * @private / index: 0, / * @private / openItems: [],

/**
* @private
*/
initLayout: function () {
    var me = this;
    Ext.layout.AccordionLayout.superclass.initLayout.call(me);

    var owner = me.owner;
    owner.el.un("click");
    owner.el.on("click", me.handleOwnerClick, me, { delegate: "h3." + me.headerCls });
},

onLayout: function () {
    var me = this;
    //me.expandHeight = me.getExpandHeight();
    Ext.layout.AccordionLayout.superclass.onLayout.call(me);

    this.collapseAll();
},

/**
* @private
*/
renderItem: function (item, position, target) {
    var me = this;
    item.index = me.index;

    item.removeCls(me.itemCls);
    item.addCls(me.itemCls);

    Ext.layout.AccordionLayout.superclass.renderItem.call(me, item, position, target);

    item.expandHeight = item.getHeight();

    //item.setVisible(false);

    me.wrapItem(item);

    me.index++;
},

/**
* @private
*/
moveItem: function (item, position, target) {
    target = item.el.refs.itemWrap;
    position = 1;

    Ext.layout.AccordionLayout.superclass.moveItem.call(this, item, position, target);
},

/**
* @private
*/
wrapItem: function (item) {
    var me = this,
        itemWrap = item.el.wrap({
            style: "-webkit-transition: height " + me.animDuration + "ms " + me.easing + "; overflow: hidden;"
        }),
        parent = itemWrap.wrap({
            cls: "section"
        }),
        header = parent.createChild({
            tag: "h3",
            cls: me.headerCls,
            html: item.title
        }, itemWrap),
        arrow = header.createChild({
            tag: "div",
            cls: me.arrowCls
        });

    item.el.refs = {
        itemWrap: itemWrap,
        parentWrap: parent,
        header: header,
        arrow: arrow
    };
},

expandItem: function (item) {
    var me = this;
    item = me.parseActiveItem(item);

    if (!item.fireEvent("beforeactivate", me.owner, item, me.activeItem)) { return false; }
    if (!item.fireEvent("beforeexpand", me.owner, item, me.activeItem)) { return false; }

    var el = item.el,
        arrow = el.refs.arrow,
        height;

    me.activeItem = item;

    if (me.fill) {
        height = me.expandHeight
    } else {
        height = (item.expandHeight > 0) ? item.expandHeight : item.getHeight();
    }

    if (height > me.maxHeight) { height = me.maxHeight; }
    if (height < me.minHeight) { height = me.minHeight; }

    el.refs.itemWrap.setHeight(height);

    me.rotateArrow(arrow, 90);

    new Ext.util.DelayedTask(function () {
        item.setVisible(true);
        item.fireEvent("activate", me.owner, item, me.activeItem);
        item.fireEvent("expand", me.owner, item, me.activeItem);
    }, me).delay(me.animDuration);
},

collapseItem: function (item) {
    var me = this;
    item = me.parseActiveItem(item);

    if (!item.fireEvent("beforedeactivate", me.owner, item, me.activeItem)) { return false; }
    if (!item.fireEvent("beforecollapse", me.owner, item, me.activeItem)) { return false; }

    var el = item.el,
        arrow = el.refs.arrow;

    me.activeItem = undefined;

    el.refs.itemWrap.setHeight(0);

    me.rotateArrow(arrow, 0);

    new Ext.util.DelayedTask(function () {
        item.fireEvent("deactivate", me.owner, item, me.activeItem);
        item.fireEvent("collapse", me.owner, item, me.activeItem);
    }, me).delay(me.animDuration);
},

/**
* Return the active (visible) component in the layout.
* @returns {Ext.Component}
*/
getActiveItem: function () {
    var me = this;

    if (!me.activeItem && me.owner) {
        me.activeItem = me.parseActiveItem(me.owner.activeItem);
    }
    if (me.activeItem && me.owner.items.items.indexOf(me.activeItem) != -1) {
        return me.activeItem;
    }
    return null;
},

/**
* @private
*/
parseActiveItem: function (item) {
    var me = this;

    if (item && item.isComponent) {
        return item;
    } else if (typeof item == 'number' || item == undefined) {
        return me.getLayoutItems()[item || 0];
    } else {
        return me.owner.getComponent(item);
    }
},

/**
* @private
*/
handleOwnerClick: function (e, t) {
    e.stopEvent();
    var me = this,
        el = Ext.get(t),
        itemEl = el.next().first(),
        item = me.parseActiveItem(itemEl.id);

    if (item === me.activeItem && me.allowCollapse) {
        me.collapseItem(item);
    } else {
        if (me.forceAutoCollapse) {
            me.collapseItem(me.activeItem);
        }
        me.expandItem(item);
    }

},

/**
* @private
*/
getExpandHeight: function () {
    var me = this,
        items = me.getLayoutItems(),
        numItems = items.length,
        header = items[0].el.refs.header,
        headerHeight = header.getHeight() * numItems,
        expandHeight = me.getTarget().getHeight() - headerHeight;

    return expandHeight;
},

/**
* @private
*/
rotateArrow: function (el, deg) {
    var me = this;

    el.setStyle({
        "-webkit-transform": "rotate(" + deg + "deg)",
        "-webkit-transition-property": "-webkit-transform",
        "-webkit-transition-duration": me.animDuration + "ms",
        "-webkit-transition-timing-function": me.easing
    });
},

collapseAll: function () {
    var me = this,
        items = me.getLayoutItems()

    Ext.each(items, function (item) {
        me.collapseItem(item);
    });
}

});

Ext.regLayout("accordion", Ext.layout.AccordionLayout);

Here is my CSS: .x-layout-accordion h3 { font-size: .85em; font-weight: bold; /border-bottom: 1px solid #D1D1D1; / padding: 7px; text-align: left; }

.x-accordion-header { width: 100%; color: #ffffff; background-image: -webkit-gradient(linear,0% 0,0% 100%,color-stop(0%,#96A9C0),color-stop(2%,#5A7596),color-stop(100%,#394B5F)); }

.x-accordion-arrow { display: inline; float: right; width: 20px; height: 20px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAeVJREFUeNqM1E9Ik3Ecx/FtuIHgaMOZMAwzKBMiD4UsiaBDB+kgJUMIcXYQunnpEJ0C/14SvHmpSxcvHjoIg6hdWixZRAcdtMMciGgozMDTFk/v7/qOfnv47Zk/eMGe777fzzP2/J6f33EcX4vVjwcYQq/WjlDAB5RtQ35L4EW8xG2U8MUYlpuMYgB5LOFX07QEGu7gG14j7vrO1Kc90pswvzObRlDAI70OewQ2SO+uzjYFRpHHpNGcxApibUKTOhs1A+fxxtXYiW2UkULAI1RmFxqBkpzDoKXxFmrOv/UR91oEXsdXRORiHO897r7o/F9VvMVVS59kjAd40NdQ9LVeq9jRzx14iixeIGz0/ZQsCYzgwCPwBK9ctR4s4zseak0yIhL4ByGf97ppqcncNvb1Oig1CTzEZY+wYTx31TL6Wj7BD61dqb+a+nbIEwxZ/mjZKlvGQyliFh2uvpBmjDaGPmHMEjitQb/1abfa5DKbkSxzt2cRNJouYQ+buOGxrYI6KxlNp807nOGZXk/oH73R5oGtowtT7tMmgrS+Ruc5GMLam9bZej1g3KmCx6jqAZpC3PKLpDajPTWdqXgdsLLuYg4x3dinWr+AbhxjDZ/Pc2I3lpzK95HQIJ8G53QflmxDfwUYAKNRwbDSoyXQAAAAAElFTkSuQmCC'); }

Let me know how this works...but i think there may still be some issues with rendering child components.

erikkaplun commented 13 years ago

Can you please post an example of how you are using the accordion_container_hidden and accordion_container CSS classes?

And I'm not sure if I understand this correctly — does that CSS class trick solve the issue for you or it's for another purpose?

ghost commented 13 years ago

I already did up above. cmp.removeCls('accordion_container_hidden').addCls('accordion_container');

where cmp = a reference to the your new Ext.Panel based on the accordion layout. You call the above code right after your code renders the accordion with the -hidden class defined above (not really hidden, just different z-index).

erikkaplun commented 13 years ago

That CSS trick did not change the behaviour at all. Did it solve the original issue for you? Also, did you accidentally close the ticket?

ghost commented 13 years ago

Depends what you mean by the "original issue". Rendering the accordion in a hidden manner means that you will not be able to determine the heights of any of the collapsed item panels. The CSS trick allows me to render the full panel with the items that are drawn in those panels, behind the scenes so you don't see it...then uses the collapseAll method to collapse all panels that rendered in an open state, and then display a fully collapsed accordion layout. This is working.

As far as rendering child Touch components in these accordion panels, I have not done much there...other than draw HTML sub-menus, so I'm afraid I can't help you there.

Not following you on the "accidentally close the ticket", what do you mean? I never had a ticket open on any of this. Have just been trading messages back with Mitchell on the functionality of the accordion layout.

erikkaplun commented 13 years ago

I opened an issue because there's a bug with rendering items in an accordion. This issue still remains unsolved, so I don't see why you closed it.

ghost commented 13 years ago

For the second time, I did NOT open or close a ticket, not even sure where or how that is done...so you can stop saying that any time now.

erikkaplun commented 13 years ago

I'm a bit confused: it explicitly says "Bucs closed the issue about 18 hours ago". Are you also viewing this at https://github.com/mitchellsimoens/Ext.layout.AccordionLayout/issues/1 ?

mitchellsimoens commented 13 years ago

@eallik drop it. Yes it does say the issue was closed by Bucs. I am aware of it because Bucs brought it to my attention but working for Sencha means long hours so I don't have all the time in the world to work on my ux/plugins. We are going to be starting devel on ST 2.0 in a month or two and this layout will be worked on.

ghost commented 13 years ago

Thanks for reopening Mitchell...@eallik, I apologize. I am new to GitHub and I think I selected Comment and Close button one time after responding. I had no idea that actually marks the thread as closed.

In any event, Mitchel saved the day and has reopened. If I discover anything related to rendering child items, I will post it here.

erikkaplun commented 13 years ago

Okay, confusion resolved :) I'm really glad to hear that the accordion layout will be worked on in ST 2.0!

mitchellsimoens commented 13 years ago

I wouldn't say it's a done deal, I'm just gonna push for it :) The core devs are a lot smarter than me so issues like this won't be a problem. A List rendered fine so I never tested on Components with items like a FormPanel... sorry about that!

mitchellsimoens commented 13 years ago

Whoa, I almost hit the 'Comment & Close' button :)

ghost commented 13 years ago

lol...Yeah, I still maintain that this layout would be the single best UI improvement that Touch 2.0 could bring. I can tell you from the large clients that our sales team is selling to, they all want an accordion for some aspect of their mobile site, generally on their mobile home page as an accordion menu.

erikkaplun commented 13 years ago

@Bucs: exactly my case. I got some UI designs form the client, and the main thing was the accordion.