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
19 * This is a layout that manages multiple Panels in an expandable accordion style such that only
20 * **one Panel can be expanded at any given time**. Each Panel has built-in support for expanding and collapsing.
22 * Note: Only Ext Panels and all subclasses of Ext.panel.Panel may be used in an accordion layout Container.
25 * Ext.create('Ext.panel.Panel', {
26 * title: 'Accordion Layout',
31 * // applied to each contained panel
32 * bodyStyle: 'padding:15px'
35 * // layout-specific configs go here
36 * titleCollapse: false,
42 * html: 'Panel content!'
45 * html: 'Panel content!'
48 * html: 'Panel content!'
50 * 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',
58 itemCls: Ext.baseCSSPrefix + 'box-item ' + Ext.baseCSSPrefix + 'accordion-item',
64 * True to adjust the active item's height to fill the available space in the container, false to use the
65 * item's current height, or auto height if not explicitly set.
70 * @cfg {Boolean} autoWidth
71 * Child Panels have their width actively managed to fit within the accordion's width.
72 * @deprecated This config is ignored in ExtJS 4
77 * @cfg {Boolean} titleCollapse
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. When set to false,
80 * {@link #hideCollapseTool} should be false also.
85 * @cfg {Boolean} hideCollapseTool
86 * True to hide the contained Panels' collapse/expand toggle buttons, false to display them.
87 * When set to true, {@link #titleCollapse} is automatically set to <code>true</code>.
89 hideCollapseTool : false,
92 * @cfg {Boolean} collapseFirst
93 * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools
94 * in the contained Panels' title bars, false to render it last.
96 collapseFirst : false,
99 * @cfg {Boolean} animate
100 * True to slide the contained panels open and closed during expand/collapse using animation, false to open and
101 * close directly with no animation. Note: The layout performs animated collapsing
102 * and expanding, <i>not</i> the child Panels.
106 * @cfg {Boolean} activeOnTop
107 * Only valid when {@link #multi} is `false` and {@link #animate} is `false`.
109 * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,
110 * false to keep the panels in the rendered order.
114 * @cfg {Boolean} multi
115 * Set to <code>true</code> to enable multiple accordion items to be open at once.
119 constructor: function() {
122 me.callParent(arguments);
124 // animate flag must be false during initial render phase so we don't get animations.
125 me.initialAnimate = me.animate;
128 // Child Panels are not absolutely positioned if we are not filling, so use a different itemCls.
129 if (me.fill === false) {
130 me.itemCls = Ext.baseCSSPrefix + 'accordion-item';
134 // Cannot lay out a fitting accordion before we have been allocated a height.
135 // So during render phase, layout will not be performed.
136 beforeLayout: function() {
139 me.callParent(arguments);
141 if (!(me.owner.el.dom.style.height || me.getLayoutTargetSize().height)) {
145 me.owner.componentLayout.monitorChildren = false;
147 me.owner.setAutoScroll(true);
151 renderItems : function(items, target) {
156 targetSize = me.getLayoutTargetSize(),
159 for (; i < ln; i++) {
161 if (!comp.rendered) {
162 renderedPanels.push(comp);
164 // Set up initial properties for Panels in an accordion.
165 if (me.collapseFirst) {
166 comp.collapseFirst = me.collapseFirst;
168 if (me.hideCollapseTool) {
169 comp.hideCollapseTool = me.hideCollapseTool;
170 comp.titleCollapse = true;
172 else if (me.titleCollapse) {
173 comp.titleCollapse = me.titleCollapse;
176 delete comp.hideHeader;
177 comp.collapsible = true;
178 comp.title = comp.title || ' ';
181 comp.width = targetSize.width;
186 // If there is an expanded item, all others must be rendered collapsed.
187 if (me.expandedItem !== undefined) {
188 comp.collapsed = true;
190 // Otherwise expand the first item with collapsed explicitly configured as false
191 else if (comp.hasOwnProperty('collapsed') && comp.collapsed === false) {
195 comp.collapsed = true;
197 // If we are fitting, then intercept expand/collapse requests.
199 show: me.onComponentShow,
200 beforeexpand: me.onComponentExpand,
201 beforecollapse: me.onComponentCollapse,
206 comp.animCollapse = me.initialAnimate;
207 comp.autoHeight = true;
208 comp.autoScroll = false;
210 comp.border = comp.collapsed;
214 // If no collapsed:false Panels found, make the first one expanded.
215 if (ln && me.expandedItem === undefined) {
218 comp.collapsed = comp.border = false;
224 // Render all Panels.
225 me.callParent(arguments);
227 // Postprocess rendered Panels.
228 ln = renderedPanels.length;
229 for (i = 0; i < ln; i++) {
230 comp = renderedPanels[i];
232 // Delete the dimension property so that our align: 'stretch' processing manages the width from here
235 comp.header.addCls(Ext.baseCSSPrefix + 'accordion-hd');
236 comp.body.addCls(Ext.baseCSSPrefix + 'accordion-body');
240 onLayout: function() {
245 me.callParent(arguments);
247 var targetSize = me.getLayoutTargetSize(),
248 items = me.getVisibleItems(),
252 for (; i < len; i++) {
254 if (comp.collapsed) {
255 items[i].setWidth(targetSize.width);
257 items[i].setSize(null, null);
261 me.updatePanelClasses();
266 updatePanelClasses: function() {
267 var children = this.getLayoutItems(),
268 ln = children.length,
269 siblingCollapsed = true,
272 for (i = 0; i < ln; i++) {
275 // Fix for EXTJSIV-3724. Windows only.
276 // Collapsing the Psnel's el to a size which only allows a single hesder to be visible, scrolls the header out of view.
278 child.el.dom.scrollTop = 0;
281 if (siblingCollapsed) {
282 child.header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
285 child.header.addCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
288 if (i + 1 == ln && child.collapsed) {
289 child.header.addCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
292 child.header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
294 siblingCollapsed = child.collapsed;
298 animCallback: function(){
299 Ext.Array.forEach(this.toCollapse, function(comp){
300 comp.fireEvent('collapse', comp);
303 Ext.Array.forEach(this.toExpand, function(comp){
304 comp.fireEvent('expand', comp);
308 setupEvents: function(){
309 this.toCollapse = [];
313 // When a Component expands, adjust the heights of the other Components to be just enough to accommodate
315 // The expanded Component receives the only flex value, and so gets all remaining space.
316 onComponentExpand: function(toExpand) {
318 it = me.owner.items.items,
324 for (; i < len; i++) {
326 if (comp === toExpand && comp.collapsed) {
327 me.setExpanded(comp);
328 } else if (!me.multi && (comp.rendered && comp.header.rendered && comp !== toExpand && !comp.collapsed)) {
329 me.setCollapsed(comp);
333 me.animate = me.initialAnimate;
334 if (me.activeOnTop) {
335 // insert will trigger a layout
336 me.owner.insert(0, toExpand);
344 onComponentCollapse: function(comp) {
346 toExpand = comp.next() || comp.prev(),
347 expanded = me.multi ? me.owner.query('>panel:not([collapsed])') : [];
350 // If we are allowing multi, and the "toCollapse" component is NOT the only expanded Component,
351 // then ask the box layout to collapse it to its header.
353 me.setCollapsed(comp);
355 // If the collapsing Panel is the only expanded one, expand the following Component.
356 // All this is handling fill: true, so there must be at least one expanded,
357 if (expanded.length === 1 && expanded[0] === comp) {
358 me.setExpanded(toExpand);
361 me.animate = me.initialAnimate;
365 // Not allowing multi: expand the next sibling if possible, prev sibling if we collapsed the last
367 me.onComponentExpand(toExpand);
372 onComponentShow: function(comp) {
373 // Showing a Component means that you want to see it, so expand it.
374 this.onComponentExpand(comp);
377 setCollapsed: function(comp) {
378 var otherDocks = comp.getDockedItems(),
380 len = otherDocks.length,
383 // Hide all docked items except the header
384 comp.hiddenDocked = [];
385 for (; i < len; i++) {
386 dockItem = otherDocks[i];
387 if ((dockItem !== comp.header) && !dockItem.hidden) {
388 dockItem.hidden = true;
389 comp.hiddenDocked.push(dockItem);
392 comp.addCls(comp.collapsedCls);
393 comp.header.addCls(comp.collapsedHeaderCls);
394 comp.height = comp.header.getHeight();
395 comp.el.setHeight(comp.height);
396 comp.collapsed = true;
398 if (this.initialAnimate) {
399 this.toCollapse.push(comp);
401 comp.fireEvent('collapse', comp);
403 if (comp.collapseTool) {
404 comp.collapseTool.setType('expand-' + comp.getOppositeDirection(comp.collapseDirection));
408 setExpanded: function(comp) {
409 var otherDocks = comp.hiddenDocked,
410 len = otherDocks ? otherDocks.length : 0,
413 // Show temporarily hidden docked items
414 for (; i < len; i++) {
415 otherDocks[i].show();
418 // If it was an initial native collapse which hides the body
419 if (!comp.body.isVisible()) {
422 delete comp.collapsed;
424 delete comp.componentLayout.lastComponentSize;
425 comp.suspendLayout = false;
427 comp.removeCls(comp.collapsedCls);
428 comp.header.removeCls(comp.collapsedHeaderCls);
429 if (this.initialAnimate) {
430 this.toExpand.push(comp);
432 comp.fireEvent('expand', comp);
434 if (comp.collapseTool) {
435 comp.collapseTool.setType('collapse-' + comp.collapseDirection);
437 comp.setAutoScroll(comp.initialConfig.autoScroll);