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