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