Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / docs / source / Accordion.html
1 <!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-layout.container.Accordion'>/**
2 </span> * @class Ext.layout.container.Accordion
3  * @extends Ext.layout.container.VBox
4  * &lt;p&gt;This is a layout that manages multiple Panels in an expandable accordion style such that only
5  * &lt;b&gt;one Panel can be expanded at any given time&lt;/b&gt;. Each Panel has built-in support for expanding and collapsing.&lt;/p&gt;
6  * &lt;p&gt;Note: Only Ext.Panels &lt;b&gt;and all subclasses of Ext.panel.Panel&lt;/b&gt; may be used in an accordion layout Container.&lt;/p&gt;
7  * {@img Ext.layout.container.Accordion/Ext.layout.container.Accordion.png Ext.layout.container.Accordion container layout}
8  * &lt;p&gt;Example usage:&lt;/p&gt;
9  * &lt;pre&gt;&lt;code&gt;
10 Ext.create('Ext.panel.Panel', {
11     title: 'Accordion Layout',
12     width: 300,
13     height: 300,
14     layout:'accordion',
15     defaults: {
16         // applied to each contained panel
17         bodyStyle: 'padding:15px'
18     },
19     layoutConfig: {
20         // layout-specific configs go here
21         titleCollapse: false,
22         animate: true,
23         activeOnTop: true
24     },
25     items: [{
26         title: 'Panel 1',
27         html: 'Panel content!'
28     },{
29         title: 'Panel 2',
30         html: 'Panel content!'
31     },{
32         title: 'Panel 3',
33         html: 'Panel content!'
34     }],
35     renderTo: Ext.getBody()
36 });
37 &lt;/code&gt;&lt;/pre&gt;
38  */
39 Ext.define('Ext.layout.container.Accordion', {
40     extend: 'Ext.layout.container.VBox',
41     alias: ['layout.accordion'],
42     alternateClassName: 'Ext.layout.AccordionLayout',
43     
44     align: 'stretch',
45
46 <span id='Ext-layout.container.Accordion-cfg-fill'>    /**
47 </span>     * @cfg {Boolean} fill
48      * True to adjust the active item's height to fill the available space in the container, false to use the
49      * item's current height, or auto height if not explicitly set (defaults to true).
50      */
51     fill : true,
52 <span id='Ext-layout.container.Accordion-cfg-autoWidth'>    /**
53 </span>     * @cfg {Boolean} autoWidth
54      * &lt;p&gt;&lt;b&gt;This config is ignored in ExtJS 4.x.&lt;/b&gt;&lt;/p&gt;
55      * Child Panels have their width actively managed to fit within the accordion's width.
56      */
57     autoWidth : true,
58 <span id='Ext-layout.container.Accordion-cfg-titleCollapse'>    /**
59 </span>     * @cfg {Boolean} titleCollapse
60      * &lt;p&gt;&lt;b&gt;Not implemented in PR2.&lt;/b&gt;&lt;/p&gt;
61      * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow
62      * expand/collapse only when the toggle tool button is clicked (defaults to true).  When set to false,
63      * {@link #hideCollapseTool} should be false also.
64      */
65     titleCollapse : true,
66 <span id='Ext-layout.container.Accordion-cfg-hideCollapseTool'>    /**
67 </span>     * @cfg {Boolean} hideCollapseTool
68      * True to hide the contained Panels' collapse/expand toggle buttons, false to display them (defaults to false).
69      * When set to true, {@link #titleCollapse} is automatically set to &lt;code&gt;true&lt;/code&gt;.
70      */
71     hideCollapseTool : false,
72 <span id='Ext-layout.container.Accordion-cfg-collapseFirst'>    /**
73 </span>     * @cfg {Boolean} collapseFirst
74      * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools
75      * in the contained Panels' title bars, false to render it last (defaults to false).
76      */
77     collapseFirst : false,
78 <span id='Ext-layout.container.Accordion-cfg-animate'>    /**
79 </span>     * @cfg {Boolean} animate
80      * True to slide the contained panels open and closed during expand/collapse using animation, false to open and
81      * close directly with no animation (defaults to &lt;code&gt;true&lt;/code&gt;). Note: The layout performs animated collapsing
82      * and expanding, &lt;i&gt;not&lt;/i&gt; the child Panels.
83      */
84     animate : true,
85 <span id='Ext-layout.container.Accordion-cfg-activeOnTop'>    /**
86 </span>     * @cfg {Boolean} activeOnTop
87      * &lt;p&gt;&lt;b&gt;Not implemented in PR4.&lt;/b&gt;&lt;/p&gt;
88      * &lt;p&gt;Only valid when {@link #multi&quot; is &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
89      * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,
90      * false to keep the panels in the rendered order. &lt;b&gt;This is NOT compatible with &quot;animate:true&quot;&lt;/b&gt; (defaults to false).
91      */
92     activeOnTop : false,
93 <span id='Ext-layout.container.Accordion-cfg-multi'>    /**
94 </span>     * @cfg {Boolean} multi
95      * Defaults to &lt;code&gt;false&lt;/code&gt;. Set to &lt;code&gt;true&lt;/code&gt; to enable multiple accordion items to be open at once.
96      */
97     multi: false,
98
99     constructor: function() {
100         var me = this;
101
102         me.callParent(arguments);
103
104         // animate flag must be false during initial render phase so we don't get animations.
105         me.initialAnimate = me.animate;
106         me.animate = false;
107
108         // Child Panels are not absolutely positioned if we are not filling, so use a different itemCls.
109         if (me.fill === false) {
110             me.itemCls = Ext.baseCSSPrefix + 'accordion-item';
111         }
112     },
113
114     // Cannot lay out a fitting accordion before we have been allocated a height.
115     // So during render phase, layout will not be performed.
116     beforeLayout: function() {
117         var me = this;
118
119         me.callParent(arguments);
120         if (me.fill) {
121             if (!me.owner.el.dom.style.height) {
122                 return false;
123             }
124         } else {
125             me.owner.componentLayout.monitorChildren = false;
126             me.autoSize = true;
127             me.owner.setAutoScroll(true);
128         }
129     },
130
131     renderItems : function(items, target) {
132         var me = this,
133             ln = items.length,
134             i = 0,
135             comp,
136             targetSize = me.getLayoutTargetSize(),
137             renderedPanels = [],
138             border;
139
140         for (; i &lt; ln; i++) {
141             comp = items[i];
142             if (!comp.rendered) {
143                 renderedPanels.push(comp);
144
145                 // Set up initial properties for Panels in an accordion.
146                 if (me.collapseFirst) {
147                     comp.collapseFirst = me.collapseFirst;
148                 }
149                 if (me.hideCollapseTool) {
150                     comp.hideCollapseTool = me.hideCollapseTool;
151                     comp.titleCollapse = true;
152                 }
153                 else if (me.titleCollapse) {
154                     comp.titleCollapse = me.titleCollapse;
155                 }
156
157                 delete comp.hideHeader;
158                 comp.collapsible = true;
159                 comp.title = comp.title || '&amp;#160;';
160                 comp.setBorder(false);
161
162                 // Set initial sizes
163                 comp.width = targetSize.width;
164                 if (me.fill) {
165                     delete comp.height;
166                     delete comp.flex;
167
168                     // If there is an expanded item, all others must be rendered collapsed.
169                     if (me.expandedItem !== undefined) {
170                         comp.collapsed = true;
171                     }
172                     // Otherwise expand the first item with collapsed explicitly configured as false
173                     else if (comp.collapsed === false) {
174                         comp.flex = 1;
175                         me.expandedItem = i;
176                     } else {
177                         comp.collapsed = true;
178                     }
179                 } else {
180                     delete comp.flex;
181                     comp.animCollapse = me.initialAnimate;
182                     comp.autoHeight = true;
183                     comp.autoScroll = false;
184                 }
185             }
186         }
187
188         // If no collapsed:false Panels found, make the first one expanded.
189         if (ln &amp;&amp; me.expandedItem === undefined) {
190             me.expandedItem = 0;
191             comp = items[0];
192             comp.collapsed = false;
193             if (me.fill) {
194                 comp.flex = 1;
195             }
196         }
197         
198         // Render all Panels.
199         me.callParent(arguments);
200                 
201         // Postprocess rendered Panels.
202         ln = renderedPanels.length;
203         for (i = 0; i &lt; ln; i++) {
204             comp = renderedPanels[i];
205
206             // Delete the dimension property so that our align: 'stretch' processing manages the width from here
207             delete comp.width;
208
209             comp.header.addCls(Ext.baseCSSPrefix + 'accordion-hd');
210             comp.body.addCls(Ext.baseCSSPrefix + 'accordion-body');
211             
212             // If we are fitting, then intercept expand/collapse requests. 
213             if (me.fill) {
214                 me.owner.mon(comp, {
215                     show: me.onComponentShow,
216                     beforeexpand: me.onComponentExpand,
217                     beforecollapse: me.onComponentCollapse,
218                     scope: me
219                 });
220             }
221         }
222     },
223
224     onLayout: function() {
225         var me = this;
226         
227         me.updatePanelClasses();
228                 
229         if (me.fill) {
230             me.callParent(arguments);
231         } else {
232             var targetSize = me.getLayoutTargetSize(),
233                 items = me.getVisibleItems(),
234                 len = items.length,
235                 i = 0, comp;
236
237             for (; i &lt; len; i++) {
238                 comp = items[i];
239                 if (comp.collapsed) {
240                     items[i].setWidth(targetSize.width);
241                 } else {
242                     items[i].setSize(null, null);
243                 }
244             }
245         }
246         
247         return me;
248     },
249     
250     updatePanelClasses: function() {
251         var children = this.getLayoutItems(),
252             ln = children.length,
253             siblingCollapsed = true,
254             i, child;
255             
256         for (i = 0; i &lt; ln; i++) {
257             child = children[i];
258             if (!siblingCollapsed) {
259                 child.header.addCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
260             }
261             else {
262                 child.header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
263             }
264             if (i + 1 == ln &amp;&amp; child.collapsed) {
265                 child.header.addCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
266             }
267             else {
268                 child.header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
269             }
270             siblingCollapsed = child.collapsed;
271         }
272     },
273
274     // When a Component expands, adjust the heights of the other Components to be just enough to accommodate
275     // their headers.
276     // The expanded Component receives the only flex value, and so gets all remaining space.
277     onComponentExpand: function(toExpand) {
278         var me = this,
279             it = me.owner.items.items,
280             len = it.length,
281             i = 0,
282             comp;
283
284         for (; i &lt; len; i++) {
285             comp = it[i];
286             if (comp === toExpand &amp;&amp; comp.collapsed) {
287                 me.setExpanded(comp);
288             } else if (!me.multi &amp;&amp; (comp.rendered &amp;&amp; comp.header.rendered &amp;&amp; comp !== toExpand &amp;&amp; !comp.collapsed)) {
289                 me.setCollapsed(comp);
290             }
291         }
292         
293         me.animate = me.initialAnimate;
294         me.layout();
295         me.animate = false;
296         return false;
297     },
298
299     onComponentCollapse: function(comp) {
300         var me = this,
301             toExpand = comp.next() || comp.prev(),
302             expanded = me.multi ? me.owner.query('&gt;panel:not([collapsed])') : [];
303
304         // If we are allowing multi, and the &quot;toCollapse&quot; component is NOT the only expanded Component,
305         // then ask the box layout to collapse it to its header.
306         if (me.multi) {
307             me.setCollapsed(comp);
308
309             // If the collapsing Panel is the only expanded one, expand the following Component.
310             // All this is handling fill: true, so there must be at least one expanded,
311             if (expanded.length === 1 &amp;&amp; expanded[0] === comp) {
312                 me.setExpanded(toExpand);
313             }
314             
315             me.animate = me.initialAnimate;
316             me.layout();
317             me.animate = false;
318         }
319         // Not allowing multi: expand the next sibling if possible, prev sibling if we collapsed the last
320         else if (toExpand) {
321             me.onComponentExpand(toExpand);
322         }
323         return false;
324     },
325
326     onComponentShow: function(comp) {
327         // Showing a Component means that you want to see it, so expand it.
328         this.onComponentExpand(comp);
329     },
330
331     setCollapsed: function(comp) {
332         var otherDocks = comp.getDockedItems(),
333             dockItem,
334             len = otherDocks.length,
335             i = 0;
336
337         // Hide all docked items except the header
338         comp.hiddenDocked = [];
339         for (; i &lt; len; i++) {
340             dockItem = otherDocks[i];
341             if ((dockItem !== comp.header) &amp;&amp; !dockItem.hidden) {
342                 dockItem.hidden = true;
343                 comp.hiddenDocked.push(dockItem);
344             }
345         }
346         comp.addCls(comp.collapsedCls);
347         comp.header.addCls(comp.collapsedHeaderCls);
348         comp.height = comp.header.getHeight();
349         comp.el.setHeight(comp.height);
350         comp.collapsed = true;
351         delete comp.flex;
352         comp.fireEvent('collapse', comp);
353         if (comp.collapseTool) {
354             comp.collapseTool.setType('expand-' + comp.getOppositeDirection(comp.collapseDirection));
355         }
356     },
357
358     setExpanded: function(comp) {
359         var otherDocks = comp.hiddenDocked,
360             len = otherDocks ? otherDocks.length : 0,
361             i = 0;
362
363         // Show temporarily hidden docked items
364         for (; i &lt; len; i++) {
365             otherDocks[i].hidden = false;
366         }
367
368         // If it was an initial native collapse which hides the body
369         if (!comp.body.isVisible()) {
370             comp.body.show();
371         }
372         delete comp.collapsed;
373         delete comp.height;
374         delete comp.componentLayout.lastComponentSize;
375         comp.suspendLayout = false;
376         comp.flex = 1;
377         comp.removeCls(comp.collapsedCls);
378         comp.header.removeCls(comp.collapsedHeaderCls);
379         comp.fireEvent('expand', comp);
380         if (comp.collapseTool) {
381             comp.collapseTool.setType('collapse-' + comp.collapseDirection);
382         }
383         comp.setAutoScroll(comp.initialConfig.autoScroll);
384     }
385 });</pre></pre></body></html>