Card.html
14.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
<script type="text/javascript">
function highlight() {
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
}
</script>
</head>
<body onload="prettyPrint(); highlight();">
<pre class="prettyprint lang-js"><span id='Ext-layout-container-Card'>/**
</span> * This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be
* visible at any given time. This layout style is most commonly used for wizards, tab implementations, etc.
* This class is intended to be extended or created via the layout:'card' {@link Ext.container.Container#layout} config,
* and should generally not need to be created directly via the new keyword.
*
* The CardLayout's focal method is {@link #setActiveItem}. Since only one panel is displayed at a time,
* the only way to move from one Component to the next is by calling setActiveItem, passing the next panel to display
* (or its id or index). The layout itself does not provide a user interface for handling this navigation,
* so that functionality must be provided by the developer.
*
* To change the active card of a container, call the setActiveItem method of its layout:
*
* @example
* var p = Ext.create('Ext.panel.Panel', {
* layout: 'card',
* items: [
* { html: 'Card 1' },
* { html: 'Card 2' }
* ],
* renderTo: Ext.getBody()
* });
*
* p.getLayout().setActiveItem(1);
*
* In the following example, a simplistic wizard setup is demonstrated. A button bar is added
* to the footer of the containing panel to provide navigation buttons. The buttons will be handled by a
* common navigation routine. Note that other uses of a CardLayout (like a tab control) would require a
* completely different implementation. For serious implementations, a better approach would be to extend
* CardLayout to provide the custom functionality needed.
*
* @example
* var navigate = function(panel, direction){
* // This routine could contain business logic required to manage the navigation steps.
* // It would call setActiveItem as needed, manage navigation button state, handle any
* // branching logic that might be required, handle alternate actions like cancellation
* // or finalization, etc. A complete wizard implementation could get pretty
* // sophisticated depending on the complexity required, and should probably be
* // done as a subclass of CardLayout in a real-world implementation.
* var layout = panel.getLayout();
* layout[direction]();
* Ext.getCmp('move-prev').setDisabled(!layout.getPrev());
* Ext.getCmp('move-next').setDisabled(!layout.getNext());
* };
*
* Ext.create('Ext.panel.Panel', {
* title: 'Example Wizard',
* width: 300,
* height: 200,
* layout: 'card',
* bodyStyle: 'padding:15px',
* defaults: {
* // applied to each contained panel
* border: false
* },
* // just an example of one possible navigation scheme, using buttons
* bbar: [
* {
* id: 'move-prev',
* text: 'Back',
* handler: function(btn) {
* navigate(btn.up("panel"), "prev");
* },
* disabled: true
* },
* '->', // greedy spacer so that the buttons are aligned to each side
* {
* id: 'move-next',
* text: 'Next',
* handler: function(btn) {
* navigate(btn.up("panel"), "next");
* }
* }
* ],
* // the panels (or "cards") within the layout
* items: [{
* id: 'card-0',
* html: '<h1>Welcome to the Wizard!</h1><p>Step 1 of 3</p>'
* },{
* id: 'card-1',
* html: '<p>Step 2 of 3</p>'
* },{
* id: 'card-2',
* html: '<h1>Congratulations!</h1><p>Step 3 of 3 - Complete</p>'
* }],
* renderTo: Ext.getBody()
* });
*/
Ext.define('Ext.layout.container.Card', {
/* Begin Definitions */
extend: 'Ext.layout.container.Fit',
alternateClassName: 'Ext.layout.CardLayout',
alias: 'layout.card',
/* End Definitions */
type: 'card',
hideInactive: true,
<span id='Ext-layout-container-Card-cfg-deferredRender'> /**
</span> * @cfg {Boolean} deferredRender
* True to render each contained item at the time it becomes active, false to render all contained items
* as soon as the layout is rendered (defaults to false). If there is a significant amount of content or
* a lot of heavy controls being rendered into panels that are not displayed by default, setting this to
* true might improve performance.
*/
deferredRender : false,
getRenderTree: function () {
var me = this,
activeItem = me.getActiveItem();
if (activeItem) {
// If they veto the activate, we have no active item
if (activeItem.hasListeners.beforeactivate && activeItem.fireEvent('beforeactivate', activeItem) === false) {
// We must null our activeItem reference, AND the one in our owning Container.
// Because upon layout invalidation, renderChildren will use this.getActiveItem which
// uses this.activeItem || this.owner.activeItem
activeItem = me.activeItem = me.owner.activeItem = null;
}
// Item is to be the active one. Fire event after it is first layed out
else if (activeItem.hasListeners.activate) {
activeItem.on({
boxready: function() {
activeItem.fireEvent('activate', activeItem);
},
single: true
});
}
if (me.deferredRender) {
if (activeItem) {
return me.getItemsRenderTree([activeItem]);
}
} else {
return me.callParent(arguments);
}
}
},
renderChildren: function () {
var me = this,
active = me.getActiveItem();
if (!me.deferredRender) {
me.callParent();
} else if (active) {
// ensure the active item is configured for the layout
me.renderItems([active], me.getRenderTarget());
}
},
isValidParent : function(item, target, position) {
// Note: Card layout does not care about order within the target because only one is ever visible.
// We only care whether the item is a direct child of the target.
var itemEl = item.el ? item.el.dom : Ext.getDom(item);
return (itemEl && itemEl.parentNode === (target.dom || target)) || false;
},
<span id='Ext-layout-container-Card-method-getActiveItem'> /**
</span> * Return the active (visible) component in the layout.
* @returns {Ext.Component}
*/
getActiveItem: function() {
var me = this,
// Ensure the calculated result references a Component
result = me.parseActiveItem(me.activeItem || (me.owner && me.owner.activeItem));
// Sanitize the result in case the active item is no longer there.
if (result && me.owner.items.indexOf(result) != -1) {
me.activeItem = result;
} else {
me.activeItem = null;
}
return me.activeItem;
},
// @private
parseActiveItem: function(item) {
if (item && item.isComponent) {
return item;
} else if (typeof item == 'number' || item === undefined) {
return this.getLayoutItems()[item || 0];
} else {
return this.owner.getComponent(item);
}
},
// @private. Called before both dynamic render, and bulk render.
// Ensure that the active item starts visible, and inactive ones start invisible
configureItem: function(item) {
if (item === this.getActiveItem()) {
item.hidden = false;
} else {
item.hidden = true;
}
this.callParent(arguments);
},
onRemove: function(component) {
var me = this;
if (component === me.activeItem) {
me.activeItem = null;
}
},
// @private
getAnimation: function(newCard, owner) {
var newAnim = (newCard || {}).cardSwitchAnimation;
if (newAnim === false) {
return false;
}
return newAnim || owner.cardSwitchAnimation;
},
<span id='Ext-layout-container-Card-method-getNext'> /**
</span> * Return the active (visible) component in the layout to the next card
* @returns {Ext.Component} The next component or false.
*/
getNext: function() {
//NOTE: Removed the JSDoc for this function's arguments because it is not actually supported in 4.0. This
//should come back in 4.1
var wrap = arguments[0],
items = this.getLayoutItems(),
index = Ext.Array.indexOf(items, this.activeItem);
return items[index + 1] || (wrap ? items[0] : false);
},
<span id='Ext-layout-container-Card-method-next'> /**
</span> * Sets the active (visible) component in the layout to the next card
* @return {Ext.Component} the activated component or false when nothing activated.
*/
next: function() {
//NOTE: Removed the JSDoc for this function's arguments because it is not actually supported in 4.0. This
//should come back in 4.1
var anim = arguments[0],
wrap = arguments[1];
return this.setActiveItem(this.getNext(wrap), anim);
},
<span id='Ext-layout-container-Card-method-getPrev'> /**
</span> * Return the active (visible) component in the layout to the previous card
* @returns {Ext.Component} The previous component or false.
*/
getPrev: function() {
//NOTE: Removed the JSDoc for this function's arguments because it is not actually supported in 4.0. This
//should come back in 4.1
var wrap = arguments[0],
items = this.getLayoutItems(),
index = Ext.Array.indexOf(items, this.activeItem);
return items[index - 1] || (wrap ? items[items.length - 1] : false);
},
<span id='Ext-layout-container-Card-method-prev'> /**
</span> * Sets the active (visible) component in the layout to the previous card
* @return {Ext.Component} the activated component or false when nothing activated.
*/
prev: function() {
//NOTE: Removed the JSDoc for this function's arguments because it is not actually supported in 4.0. This
//should come back in 4.1
var anim = arguments[0],
wrap = arguments[1];
return this.setActiveItem(this.getPrev(wrap), anim);
},
<span id='Ext-layout-container-Card-method-setActiveItem'> /**
</span> * Makes the given card active.
*
* var card1 = Ext.create('Ext.panel.Panel', {itemId: 'card-1'});
* var card2 = Ext.create('Ext.panel.Panel', {itemId: 'card-2'});
* var panel = Ext.create('Ext.panel.Panel', {
* layout: 'card',
* activeItem: 0,
* items: [card1, card2]
* });
* // These are all equivalent
* panel.getLayout().setActiveItem(card2);
* panel.getLayout().setActiveItem('card-2');
* panel.getLayout().setActiveItem(1);
*
* @param {Ext.Component/Number/String} newCard The component, component {@link Ext.Component#id id},
* {@link Ext.Component#itemId itemId}, or index of component.
* @return {Ext.Component} the activated component or false when nothing activated.
* False is returned also when trying to activate an already active card.
*/
setActiveItem: function(newCard) {
var me = this,
owner = me.owner,
oldCard = me.activeItem,
rendered = owner.rendered,
newIndex;
newCard = me.parseActiveItem(newCard);
newIndex = owner.items.indexOf(newCard);
// If the card is not a child of the owner, then add it.
// Without doing a layout!
if (newIndex == -1) {
newIndex = owner.items.items.length;
Ext.suspendLayouts();
newCard = owner.add(newCard);
Ext.resumeLayouts();
}
// Is this a valid, different card?
if (newCard && oldCard != newCard) {
// Fire the beforeactivate and beforedeactivate events on the cards
if (newCard.fireEvent('beforeactivate', newCard, oldCard) === false) {
return false;
}
if (oldCard && oldCard.fireEvent('beforedeactivate', oldCard, newCard) === false) {
return false;
}
if (rendered) {
Ext.suspendLayouts();
// If the card has not been rendered yet, now is the time to do so.
if (!newCard.rendered) {
me.renderItem(newCard, me.getRenderTarget(), owner.items.length);
}
if (oldCard) {
if (me.hideInactive) {
oldCard.hide();
oldCard.hiddenByLayout = true;
}
oldCard.fireEvent('deactivate', oldCard, newCard);
}
// Make sure the new card is shown
if (newCard.hidden) {
newCard.show();
}
// Layout needs activeItem to be correct, so set it if the show has not been vetoed
if (!newCard.hidden) {
me.activeItem = newCard;
}
Ext.resumeLayouts(true);
} else {
me.activeItem = newCard;
}
newCard.fireEvent('activate', newCard, oldCard);
return me.activeItem;
}
return false;
}
});
</pre>
</body>
</html>