Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / layout / container / Card.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
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.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be
17  * visible at any given time.  This layout style is most commonly used for wizards, tab implementations, etc.
18  * This class is intended to be extended or created via the layout:'card' {@link Ext.container.Container#layout} config,
19  * and should generally not need to be created directly via the new keyword.
20  *
21  * The CardLayout's focal method is {@link #setActiveItem}.  Since only one panel is displayed at a time,
22  * the only way to move from one Component to the next is by calling setActiveItem, passing the next panel to display
23  * (or its id or index).  The layout itself does not provide a user interface for handling this navigation,
24  * so that functionality must be provided by the developer.
25  *
26  * To change the active card of a container, call the setActiveItem method of its layout:
27  *
28  *     Ext.create('Ext.panel.Panel', {
29  *         layout: 'card',
30  *         items: [
31  *             { html: 'Card 1' },
32  *             { html: 'Card 2' }
33  *         ],
34  *         renderTo: Ext.getBody()
35  *     });
36  *
37  *     p.getLayout().setActiveItem(1);
38  *
39  * In the following example, a simplistic wizard setup is demonstrated.  A button bar is added
40  * to the footer of the containing panel to provide navigation buttons.  The buttons will be handled by a
41  * common navigation routine.  Note that other uses of a CardLayout (like a tab control) would require a
42  * completely different implementation.  For serious implementations, a better approach would be to extend
43  * CardLayout to provide the custom functionality needed.
44  *
45  *     @example
46  *     var navigate = function(panel, direction){
47  *         // This routine could contain business logic required to manage the navigation steps.
48  *         // It would call setActiveItem as needed, manage navigation button state, handle any
49  *         // branching logic that might be required, handle alternate actions like cancellation
50  *         // or finalization, etc.  A complete wizard implementation could get pretty
51  *         // sophisticated depending on the complexity required, and should probably be
52  *         // done as a subclass of CardLayout in a real-world implementation.
53  *         var layout = panel.getLayout();
54  *         layout[direction]();
55  *         Ext.getCmp('move-prev').setDisabled(!layout.getPrev());
56  *         Ext.getCmp('move-next').setDisabled(!layout.getNext());
57  *     };
58  *
59  *     Ext.create('Ext.panel.Panel', {
60  *         title: 'Example Wizard',
61  *         width: 300,
62  *         height: 200,
63  *         layout: 'card',
64  *         bodyStyle: 'padding:15px',
65  *         defaults: {
66  *             // applied to each contained panel
67  *             border: false
68  *         },
69  *         // just an example of one possible navigation scheme, using buttons
70  *         bbar: [
71  *             {
72  *                 id: 'move-prev',
73  *                 text: 'Back',
74  *                 handler: function(btn) {
75  *                     navigate(btn.up("panel"), "prev");
76  *                 },
77  *                 disabled: true
78  *             },
79  *             '->', // greedy spacer so that the buttons are aligned to each side
80  *             {
81  *                 id: 'move-next',
82  *                 text: 'Next',
83  *                 handler: function(btn) {
84  *                     navigate(btn.up("panel"), "next");
85  *                 }
86  *             }
87  *         ],
88  *         // the panels (or "cards") within the layout
89  *         items: [{
90  *             id: 'card-0',
91  *             html: '<h1>Welcome to the Wizard!</h1><p>Step 1 of 3</p>'
92  *         },{
93  *             id: 'card-1',
94  *             html: '<p>Step 2 of 3</p>'
95  *         },{
96  *             id: 'card-2',
97  *             html: '<h1>Congratulations!</h1><p>Step 3 of 3 - Complete</p>'
98  *         }],
99  *         renderTo: Ext.getBody()
100  *     });
101  */
102 Ext.define('Ext.layout.container.Card', {
103
104     /* Begin Definitions */
105
106     alias: ['layout.card'],
107     alternateClassName: 'Ext.layout.CardLayout',
108
109     extend: 'Ext.layout.container.AbstractCard',
110
111     /* End Definitions */
112
113     /**
114      * Makes the given card active.
115      *
116      *     var card1 = Ext.create('Ext.panel.Panel', {itemId: 'card-1'});
117      *     var card2 = Ext.create('Ext.panel.Panel', {itemId: 'card-2'});
118      *     var panel = Ext.create('Ext.panel.Panel', {
119      *         layout: 'card',
120      *         activeItem: 0,
121      *         items: [card1, card2]
122      *     });
123      *     // These are all equivalent
124      *     panel.getLayout().setActiveItem(card2);
125      *     panel.getLayout().setActiveItem('card-2');
126      *     panel.getLayout().setActiveItem(1);
127      *
128      * @param {Ext.Component/Number/String} newCard  The component, component {@link Ext.Component#id id},
129      * {@link Ext.Component#itemId itemId}, or index of component.
130      * @return {Ext.Component} the activated component or false when nothing activated.
131      * False is returned also when trying to activate an already active card.
132      */
133     setActiveItem: function(newCard) {
134         var me = this,
135             owner = me.owner,
136             oldCard = me.activeItem,
137             newIndex;
138
139         newCard = me.parseActiveItem(newCard);
140         newIndex = owner.items.indexOf(newCard);
141
142         // If the card is not a child of the owner, then add it
143         if (newIndex == -1) {
144             newIndex = owner.items.items.length;
145             owner.add(newCard);
146         }
147
148         // Is this a valid, different card?
149         if (newCard && oldCard != newCard) {
150             // If the card has not been rendered yet, now is the time to do so.
151             if (!newCard.rendered) {
152                 me.renderItem(newCard, me.getRenderTarget(), owner.items.length);
153                 me.configureItem(newCard, 0);
154             }
155
156             me.activeItem = newCard;
157
158             // Fire the beforeactivate and beforedeactivate events on the cards
159             if (newCard.fireEvent('beforeactivate', newCard, oldCard) === false) {
160                 return false;
161             }
162             if (oldCard && oldCard.fireEvent('beforedeactivate', oldCard, newCard) === false) {
163                 return false;
164             }
165
166             // If the card hasnt been sized yet, do it now
167             if (me.sizeAllCards) {
168                 // onLayout calls setItemBox
169                 me.onLayout();
170             }
171             else {
172                 me.setItemBox(newCard, me.getTargetBox());
173             }
174
175             me.owner.suspendLayout = true;
176
177             if (oldCard) {
178                 if (me.hideInactive) {
179                     oldCard.hide();
180                 }
181                 oldCard.fireEvent('deactivate', oldCard, newCard);
182             }
183
184             // Make sure the new card is shown
185             me.owner.suspendLayout = false;
186             if (newCard.hidden) {
187                 newCard.show();
188             } else {
189                 me.onLayout();
190             }
191
192             newCard.fireEvent('activate', newCard, oldCard);
193
194             return newCard;
195         }
196         return false;
197     },
198
199     configureItem: function(item) {
200         // Card layout only controls dimensions which IT has controlled.
201         // That calculation has to be determined at run time by examining the ownerCt's isFixedWidth()/isFixedHeight() methods
202         item.layoutManagedHeight = 0;
203         item.layoutManagedWidth = 0;
204
205         this.callParent(arguments);
206     }});