3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.layout.container.Accordion
17 * @extends Ext.layout.container.VBox
18 * <p>This is a layout that manages multiple Panels in an expandable accordion style such that only
19 * <b>one Panel can be expanded at any given time</b>. Each Panel has built-in support for expanding and collapsing.</p>
20 * <p>Note: Only Ext.Panels <b>and all subclasses of Ext.panel.Panel</b> may be used in an accordion layout Container.</p>
21 * {@img Ext.layout.container.Accordion/Ext.layout.container.Accordion.png Ext.layout.container.Accordion container layout}
22 * <p>Example usage:</p>
24 Ext.create('Ext.panel.Panel', {
25 title: 'Accordion Layout',
30 // applied to each contained panel
31 bodyStyle: 'padding:15px'
34 // layout-specific configs go here
41 html: 'Panel content!'
44 html: 'Panel content!'
47 html: 'Panel content!'
49 renderTo: Ext.getBody()
53 Ext.define('Ext.layout.container.Accordion', {
54 extend: 'Ext.layout.container.VBox',
55 alias: ['layout.accordion'],
56 alternateClassName: 'Ext.layout.AccordionLayout',
62 * True to adjust the active item's height to fill the available space in the container, false to use the
63 * item's current height, or auto height if not explicitly set (defaults to true).
67 * @cfg {Boolean} autoWidth
68 * <p><b>This config is ignored in ExtJS 4.x.</b></p>
69 * Child Panels have their width actively managed to fit within the accordion's width.
73 * @cfg {Boolean} titleCollapse
74 * <p><b>Not implemented in PR2.</b></p>
75 * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow
76 * expand/collapse only when the toggle tool button is clicked (defaults to true). When set to false,
77 * {@link #hideCollapseTool} should be false also.
81 * @cfg {Boolean} hideCollapseTool
82 * True to hide the contained Panels' collapse/expand toggle buttons, false to display them (defaults to false).
83 * When set to true, {@link #titleCollapse} is automatically set to <code>true</code>.
85 hideCollapseTool : false,
87 * @cfg {Boolean} collapseFirst
88 * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools
89 * in the contained Panels' title bars, false to render it last (defaults to false).
91 collapseFirst : false,
93 * @cfg {Boolean} animate
94 * True to slide the contained panels open and closed during expand/collapse using animation, false to open and
95 * close directly with no animation (defaults to <code>true</code>). Note: The layout performs animated collapsing
96 * and expanding, <i>not</i> the child Panels.
100 * @cfg {Boolean} activeOnTop
101 * <p><b>Not implemented in PR4.</b></p>
102 * <p>Only valid when {@link #multi" is <code>false</code>.</p>
103 * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,
104 * false to keep the panels in the rendered order. <b>This is NOT compatible with "animate:true"</b> (defaults to false).
108 * @cfg {Boolean} multi
109 * Defaults to <code>false</code>. Set to <code>true</code> to enable multiple accordion items to be open at once.
113 constructor: function() {
116 me.callParent(arguments);
118 // animate flag must be false during initial render phase so we don't get animations.
119 me.initialAnimate = me.animate;
122 // Child Panels are not absolutely positioned if we are not filling, so use a different itemCls.
123 if (me.fill === false) {
124 me.itemCls = Ext.baseCSSPrefix + 'accordion-item';
128 // Cannot lay out a fitting accordion before we have been allocated a height.
129 // So during render phase, layout will not be performed.
130 beforeLayout: function() {
133 me.callParent(arguments);
135 if (!me.owner.el.dom.style.height || !me.getLayoutTargetSize().height) {
139 me.owner.componentLayout.monitorChildren = false;
141 me.owner.setAutoScroll(true);
145 renderItems : function(items, target) {
150 targetSize = me.getLayoutTargetSize(),
154 for (; i < ln; i++) {
156 if (!comp.rendered) {
157 renderedPanels.push(comp);
159 // Set up initial properties for Panels in an accordion.
160 if (me.collapseFirst) {
161 comp.collapseFirst = me.collapseFirst;
163 if (me.hideCollapseTool) {
164 comp.hideCollapseTool = me.hideCollapseTool;
165 comp.titleCollapse = true;
167 else if (me.titleCollapse) {
168 comp.titleCollapse = me.titleCollapse;
171 delete comp.hideHeader;
172 comp.collapsible = true;
173 comp.title = comp.title || ' ';
176 comp.width = targetSize.width;
181 // If there is an expanded item, all others must be rendered collapsed.
182 if (me.expandedItem !== undefined) {
183 comp.collapsed = true;
185 // Otherwise expand the first item with collapsed explicitly configured as false
186 else if (comp.hasOwnProperty('collapsed') && comp.collapsed === false) {
190 comp.collapsed = true;
192 // If we are fitting, then intercept expand/collapse requests.
194 show: me.onComponentShow,
195 beforeexpand: me.onComponentExpand,
196 beforecollapse: me.onComponentCollapse,
201 comp.animCollapse = me.initialAnimate;
202 comp.autoHeight = true;
203 comp.autoScroll = false;
208 // If no collapsed:false Panels found, make the first one expanded.
209 if (ln && me.expandedItem === undefined) {
212 comp.collapsed = false;
218 // Render all Panels.
219 me.callParent(arguments);
221 // Postprocess rendered Panels.
222 ln = renderedPanels.length;
223 for (i = 0; i < ln; i++) {
224 comp = renderedPanels[i];
226 // Delete the dimension property so that our align: 'stretch' processing manages the width from here
229 comp.header.addCls(Ext.baseCSSPrefix + 'accordion-hd');
230 comp.body.addCls(Ext.baseCSSPrefix + 'accordion-body');
234 onLayout: function() {
239 me.callParent(arguments);
241 var targetSize = me.getLayoutTargetSize(),
242 items = me.getVisibleItems(),
246 for (; i < len; i++) {
248 if (comp.collapsed) {
249 items[i].setWidth(targetSize.width);
251 items[i].setSize(null, null);
255 me.updatePanelClasses();
260 updatePanelClasses: function() {
261 var children = this.getLayoutItems(),
262 ln = children.length,
263 siblingCollapsed = true,
266 for (i = 0; i < ln; i++) {
269 if (siblingCollapsed) {
270 child.header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
273 child.header.addCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
276 if (i + 1 == ln && child.collapsed) {
277 child.header.addCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
280 child.header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
282 siblingCollapsed = child.collapsed;
286 // When a Component expands, adjust the heights of the other Components to be just enough to accommodate
288 // The expanded Component receives the only flex value, and so gets all remaining space.
289 onComponentExpand: function(toExpand) {
291 it = me.owner.items.items,
296 for (; i < len; i++) {
298 if (comp === toExpand && comp.collapsed) {
299 me.setExpanded(comp);
300 } else if (!me.multi && (comp.rendered && comp.header.rendered && comp !== toExpand && !comp.collapsed)) {
301 me.setCollapsed(comp);
305 me.animate = me.initialAnimate;
311 onComponentCollapse: function(comp) {
313 toExpand = comp.next() || comp.prev(),
314 expanded = me.multi ? me.owner.query('>panel:not([collapsed])') : [];
316 // If we are allowing multi, and the "toCollapse" component is NOT the only expanded Component,
317 // then ask the box layout to collapse it to its header.
319 me.setCollapsed(comp);
321 // If the collapsing Panel is the only expanded one, expand the following Component.
322 // All this is handling fill: true, so there must be at least one expanded,
323 if (expanded.length === 1 && expanded[0] === comp) {
324 me.setExpanded(toExpand);
327 me.animate = me.initialAnimate;
331 // Not allowing multi: expand the next sibling if possible, prev sibling if we collapsed the last
333 me.onComponentExpand(toExpand);
338 onComponentShow: function(comp) {
339 // Showing a Component means that you want to see it, so expand it.
340 this.onComponentExpand(comp);
343 setCollapsed: function(comp) {
344 var otherDocks = comp.getDockedItems(),
346 len = otherDocks.length,
349 // Hide all docked items except the header
350 comp.hiddenDocked = [];
351 for (; i < len; i++) {
352 dockItem = otherDocks[i];
353 if ((dockItem !== comp.header) && !dockItem.hidden) {
354 dockItem.hidden = true;
355 comp.hiddenDocked.push(dockItem);
358 comp.addCls(comp.collapsedCls);
359 comp.header.addCls(comp.collapsedHeaderCls);
360 comp.height = comp.header.getHeight();
361 comp.el.setHeight(comp.height);
362 comp.collapsed = true;
364 comp.fireEvent('collapse', comp);
365 if (comp.collapseTool) {
366 comp.collapseTool.setType('expand-' + comp.getOppositeDirection(comp.collapseDirection));
370 setExpanded: function(comp) {
371 var otherDocks = comp.hiddenDocked,
372 len = otherDocks ? otherDocks.length : 0,
375 // Show temporarily hidden docked items
376 for (; i < len; i++) {
377 otherDocks[i].show();
380 // If it was an initial native collapse which hides the body
381 if (!comp.body.isVisible()) {
384 delete comp.collapsed;
386 delete comp.componentLayout.lastComponentSize;
387 comp.suspendLayout = false;
389 comp.removeCls(comp.collapsedCls);
390 comp.header.removeCls(comp.collapsedHeaderCls);
391 comp.fireEvent('expand', comp);
392 if (comp.collapseTool) {
393 comp.collapseTool.setType('collapse-' + comp.collapseDirection);
395 comp.setAutoScroll(comp.initialConfig.autoScroll);