]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/layout/container/Container.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / layout / container / Container.js
CommitLineData
6527f429
DM
1/**\r
2 * This class is intended to be extended or created via the {@link Ext.container.Container#layout layout}\r
3 * configuration property. See {@link Ext.container.Container#layout} for additional details.\r
4 */\r
5Ext.define('Ext.layout.container.Container', {\r
6 extend: 'Ext.layout.Layout',\r
7\r
8 alias: 'layout.container',\r
9\r
10 alternateClassName: 'Ext.layout.ContainerLayout',\r
11\r
12 mixins: [\r
13 'Ext.util.ElementContainer'\r
14 ],\r
15\r
16 requires: [\r
17 'Ext.XTemplate'\r
18 ],\r
19\r
20 type: 'container',\r
21\r
22 /* End Definitions */\r
23\r
24 /**\r
25 * @cfg {String} itemCls\r
26 * An optional extra CSS class that will be added to the container. This can be useful for\r
27 * adding customized styles to the container or any of its children using standard CSS\r
28 * rules. See {@link Ext.Component}.{@link Ext.Component#componentCls componentCls} also.\r
29 */\r
30\r
31 /**\r
32 * @private\r
33 * Called by an owning Panel before the Panel begins its collapse process.\r
34 * Most layouts will not need to override the default Ext.emptyFn implementation.\r
35 */\r
36 beginCollapse: Ext.emptyFn,\r
37\r
38 /**\r
39 * @private\r
40 * Called by an owning Panel before the Panel begins its expand process.\r
41 * Most layouts will not need to override the default Ext.emptyFn implementation.\r
42 */\r
43 beginExpand: Ext.emptyFn,\r
44\r
45 /**\r
46 * An object which contains boolean properties specifying which properties are to be\r
47 * animated upon flush of child Component ContextItems. For example, Accordion would\r
48 * have:\r
49 *\r
50 * {\r
51 * y: true,\r
52 * height: true\r
53 * }\r
54 *\r
55 * @private\r
56 */\r
57 animatePolicy: null,\r
58\r
59 /**\r
60 * @private\r
61 * tracks the number of child items that do not use "liquid" CSS layout\r
62 */\r
63 activeItemCount: 0,\r
64\r
65 renderTpl: [\r
66 '{%this.renderBody(out,values)%}'\r
67 ],\r
68\r
69 usesContainerHeight: true,\r
70 usesContainerWidth: true,\r
71 usesHeight: true,\r
72 usesWidth: true,\r
73\r
74 constructor: function () {\r
75 this.callParent(arguments);\r
76 this.mixins.elementCt.constructor.call(this);\r
77 },\r
78\r
79 destroy: function() {\r
80 this.callParent();\r
81 this.mixins.elementCt.destroy.call(this);\r
82 },\r
83\r
84 /**\r
85 * In addition to work done by our base classes, containers benefit from some extra\r
86 * cached data. The following properties are added to the ownerContext:\r
87 *\r
88 * - visibleItems: the result of {@link #getVisibleItems}\r
89 * - childItems: the ContextItem[] for each visible item\r
90 * - targetContext: the ContextItem for the {@link #getTarget} element\r
91 */\r
92 beginLayout: function (ownerContext) {\r
93 this.callParent(arguments);\r
94\r
95 ownerContext.targetContext = ownerContext.paddingContext = ownerContext.getEl('getTarget', this);\r
96\r
97 this.cacheChildItems(ownerContext);\r
98 },\r
99\r
100 beginLayoutCycle: function (ownerContext, firstCycle) {\r
101 var me = this;\r
102\r
103 me.callParent(arguments);\r
104\r
105 if (firstCycle) {\r
106 if (me.usesContainerHeight) {\r
107 ++ownerContext.consumersContainerHeight;\r
108 }\r
109 if (me.usesContainerWidth) {\r
110 ++ownerContext.consumersContainerWidth;\r
111 }\r
112 }\r
113 },\r
114\r
115 cacheChildItems: function (ownerContext) {\r
116 var me = this,\r
117 context, childItems, items, length, i;\r
118\r
119 // if we neither read nor set the size of our items, we can skip creation of\r
120 // the childItems array\r
121 if (me.needsItemSize || me.setsItemSize) {\r
122 context = ownerContext.context;\r
123 childItems = ownerContext.childItems = [];\r
124 items = ownerContext.visibleItems = me.getVisibleItems();\r
125 length = items.length;\r
126\r
127 for (i = 0; i < length; ++i) {\r
128 childItems.push(context.getCmp(items[i]));\r
129 }\r
130 }\r
131 },\r
132\r
133 cacheElements: function () {\r
134 var owner = this.owner;\r
135\r
136 this.attachChildEls(owner.el, owner); // from ElementContainer mixin\r
137 },\r
138\r
139 calculate: function(ownerContext) {\r
140 var props = ownerContext.props,\r
141 el = ownerContext.el;\r
142\r
143 if (ownerContext.widthModel.shrinkWrap && isNaN(props.width)) {\r
144 ownerContext.setContentWidth(el.getWidth());\r
145 }\r
146\r
147 if (ownerContext.heightModel.shrinkWrap && isNaN(props.height)) {\r
148 ownerContext.setContentHeight(el.getHeight());\r
149 }\r
150 },\r
151\r
152 /**\r
153 * Adds layout's itemCls and owning Container's itemCls\r
154 * @protected\r
155 */\r
156 configureItem: function(item) {\r
157 var me = this,\r
158 itemCls = me.itemCls,\r
159 ownerItemCls = me.owner.itemCls,\r
160 needsCopy,\r
161 addClasses;\r
162\r
163 // Effectively callParent but without the function overhead\r
164 item.ownerLayout = me;\r
165\r
166 if (itemCls) {\r
167 // itemCls can be a single class or an array\r
168 if (typeof itemCls === 'string') {\r
169 addClasses = [itemCls];\r
170 } else {\r
171 addClasses = itemCls;\r
172 needsCopy = !!addClasses;\r
173 }\r
174 }\r
175 if (ownerItemCls) {\r
176 // Add some extra logic so we don't clone the array unnecessarily\r
177 if (needsCopy) {\r
178 addClasses = Ext.Array.clone(addClasses);\r
179 }\r
180 addClasses = Ext.Array.push(addClasses || [], ownerItemCls);\r
181 }\r
182 if (addClasses) {\r
183 item.addCls(addClasses);\r
184 }\r
185 },\r
186\r
187 doRenderBody: function (out, renderData) {\r
188 // Careful! This method is bolted on to the renderTpl so all we get for context is\r
189 // the renderData! The "this" pointer is the renderTpl instance!\r
190\r
191 this.renderItems(out, renderData);\r
192 this.renderContent(out, renderData);\r
193 },\r
194\r
195 doRenderContainer: function (out, renderData) {\r
196 // Careful! This method is bolted on to the renderTpl so all we get for context is\r
197 // the renderData! The "this" pointer is the renderTpl instance!\r
198\r
199 var me = renderData.$comp.layout,\r
200 tpl = me.getRenderTpl(),\r
201 data = me.getRenderData();\r
202\r
203 tpl.applyOut(data, out);\r
204 },\r
205\r
206 doRenderItems: function (out, renderData) {\r
207 // Careful! This method is bolted on to the renderTpl so all we get for context is\r
208 // the renderData! The "this" pointer is the renderTpl instance!\r
209\r
210 var me = renderData.$layout,\r
211 tree = me.getRenderTree();\r
212\r
213 if (tree) {\r
214 Ext.DomHelper.generateMarkup(tree, out);\r
215 }\r
216 },\r
217\r
218 finishRender: function () {\r
219 var me = this,\r
220 target, items;\r
221\r
222 me.callParent();\r
223\r
224 me.cacheElements();\r
225\r
226 target = me.getRenderTarget();\r
227 items = me.getLayoutItems();\r
228\r
229 me.finishRenderItems(target, items);\r
230 },\r
231\r
232 /**\r
233 * @private\r
234 * Called for every layout in the layout context after all the layouts have been finally flushed\r
235 */\r
236 notifyOwner: function() {\r
237 //<debug>\r
238 if (!this._hasTargetWarning && this.targetCls && !this.getTarget().hasCls(this.targetCls)) {\r
239 this._hasTargetWarning = true;\r
240 Ext.log.warn('targetCls is missing. This may mean that getTargetEl() is being overridden but not applyTargetCls(). ' + this.owner.id);\r
241 }\r
242 //</debug>\r
243 this.owner.afterLayout(this);\r
244 },\r
245\r
246 /**\r
247 * Returns the container size (that of the target). Only the fixed-sized dimensions can\r
248 * be returned because the shrinkWrap dimensions are based on the contentWidth/Height\r
249 * as determined by the container layout.\r
250 *\r
251 * @param {Ext.layout.ContextItem} ownerContext The owner's context item.\r
252 * @param {Boolean} [inDom=false] True if the container size must be in the DOM.\r
253 * @return {Object} The size\r
254 * @return {Number} return.width The width\r
255 * @return {Number} return.height The height\r
256 * @protected\r
257 */\r
258 getContainerSize : function(ownerContext, inDom) {\r
259 // Subtle But Important:\r
260 //\r
261 // We don't want to call getProp/hasProp et.al. unless we in fact need that value\r
262 // for our results! If we call it and don't need it, the layout manager will think\r
263 // we depend on it and will schedule us again should it change.\r
264\r
265 var targetContext = ownerContext.targetContext,\r
266 frameInfo = targetContext.getFrameInfo(),\r
267 padding = ownerContext.paddingContext.getPaddingInfo(),\r
268 got = 0,\r
269 needed = 0,\r
270 gotWidth, gotHeight, width, height;\r
271\r
272 // In an shrinkWrap width/height case, we must not ask for any of these dimensions\r
273 // because they will be determined by contentWidth/Height which is calculated by\r
274 // this layout...\r
275\r
276 // Fit/Card layouts are able to set just the width of children, allowing child's\r
277 // resulting height to autosize the Container.\r
278 // See examples/tabs/tabs.html for an example of this.\r
279\r
280 if (!ownerContext.widthModel.shrinkWrap) {\r
281 ++needed;\r
282 width = inDom ? targetContext.getDomProp('width') : targetContext.getProp('width');\r
283 gotWidth = (typeof width === 'number');\r
284 if (gotWidth) {\r
285 ++got;\r
286 width -= frameInfo.width + padding.width;\r
287 if (width < 0) {\r
288 width = 0;\r
289 }\r
290 }\r
291 }\r
292\r
293 if (!ownerContext.heightModel.shrinkWrap) {\r
294 ++needed;\r
295 height = inDom ? targetContext.getDomProp('height') : targetContext.getProp('height');\r
296 gotHeight = (typeof height === 'number');\r
297 if (gotHeight) {\r
298 ++got;\r
299 height -= frameInfo.height + padding.height;\r
300 if (height < 0) {\r
301 height = 0;\r
302 }\r
303 }\r
304 }\r
305\r
306 return {\r
307 width: width,\r
308 height: height,\r
309 needed: needed,\r
310 got: got,\r
311 gotAll: got === needed,\r
312 gotWidth: gotWidth,\r
313 gotHeight: gotHeight\r
314 };\r
315 },\r
316\r
317 // This method is used to offset the DOM position when checking\r
318 // whether the element is a certain child of the target. This is\r
319 // required in cases where the extra elements prepended to the target\r
320 // before any of the items. An example of this is when using labelAlign: 'top'\r
321 // on a field. The label appears first in the DOM before any child items are\r
322 // created, so when we check the position we need to add an extra offset.\r
323 // Containers that create an innerCt are exempt because this new element\r
324 // preserves the order\r
325 getPositionOffset: function(position) {\r
326 if (!this.createsInnerCt) {\r
327 var offset = this.owner.itemNodeOffset;\r
328 if (offset) {\r
329 position += offset;\r
330 }\r
331 }\r
332 return position;\r
333 },\r
334\r
335 /**\r
336 * Returns an array of child components either for a render phase (Performed in the beforeLayout\r
337 * method of the layout's base class), or the layout phase (onLayout).\r
338 * @return {Ext.Component[]} of child components\r
339 */\r
340 getLayoutItems: function() {\r
341 var owner = this.owner,\r
342 items = owner && owner.items;\r
343\r
344 return (items && items.items) || [];\r
345 },\r
346\r
347 getRenderData: function () {\r
348 var comp = this.owner;\r
349\r
350 return {\r
351 $comp: comp,\r
352 $layout: this,\r
353 ownerId: comp.id\r
354 };\r
355 },\r
356\r
357 /**\r
358 * @protected\r
359 * Returns all items that are rendered\r
360 * @return {Array} All matching items\r
361 */\r
362 getRenderedItems: function() {\r
363 var me = this,\r
364 target = me.getRenderTarget(),\r
365 items = me.getLayoutItems(),\r
366 ln = items.length,\r
367 renderedItems = [],\r
368 i, item;\r
369\r
370 for (i = 0; i < ln; i++) {\r
371 item = items[i];\r
372 if (item.rendered && me.isValidParent(item, target, i)) {\r
373 renderedItems.push(item);\r
374 }\r
375 }\r
376\r
377 return renderedItems;\r
378 },\r
379\r
380 /**\r
381 * Returns the element into which rendering must take place. Defaults to the owner Container's\r
382 * target element.\r
383 *\r
384 * May be overridden in layout managers which implement an inner element.\r
385 *\r
386 * @return {Ext.dom.Element}\r
387 */\r
388 getRenderTarget: function() {\r
389 return this.owner.getTargetEl();\r
390 },\r
391\r
392 /**\r
393 * Returns the element into which extra functional DOM elements can be inserted. Defaults to the owner Component's encapsulating element.\r
394 *\r
395 * May be overridden in Component layout managers which implement a {@link #getRenderTarget component render target} which must only\r
396 * contain child components.\r
397 * @return {Ext.dom.Element}\r
398 */\r
399 getElementTarget: function() {\r
400 return this.getRenderTarget();\r
401 },\r
402\r
403 getRenderTpl: function () {\r
404 var me = this,\r
405 renderTpl = Ext.XTemplate.getTpl(this, 'renderTpl');\r
406\r
407 // Make sure all standard callout methods for the owner component are placed on the\r
408 // XTemplate instance (but only once please):\r
409 if (!renderTpl.renderContent) {\r
410 me.owner.setupRenderTpl(renderTpl);\r
411 }\r
412\r
413 return renderTpl;\r
414 },\r
415\r
416 getRenderTree: function () {\r
417 var result,\r
418 items = this.owner.items,\r
419 itemsGen,\r
420 renderCfgs = {};\r
421\r
422 do {\r
423 itemsGen = items.generation;\r
424 result = this.getItemsRenderTree(this.getLayoutItems(), renderCfgs);\r
425 } while (items.generation !== itemsGen);\r
426 return result;\r
427 },\r
428\r
429 renderChildren: function () {\r
430 var me = this,\r
431 ownerItems = me.owner.items,\r
432 target = me.getRenderTarget(),\r
433 itemsGen, items;\r
434\r
435 // During the render phase, new items may be added. Specifically, a panel will\r
436 // create a placeholder component during render if required, so we need to catch\r
437 // it here so we can render it.\r
438 do {\r
439 itemsGen = ownerItems.generation;\r
440 items = me.getLayoutItems();\r
441 me.renderItems(items, target);\r
442 } while (ownerItems.generation !== itemsGen);\r
443 },\r
444\r
445 getScrollbarsNeeded: function (width, height, contentWidth, contentHeight) {\r
446 var scrollbarSize = Ext.getScrollbarSize(),\r
447 hasWidth = typeof width === 'number',\r
448 hasHeight = typeof height === 'number',\r
449 needHorz = 0,\r
450 needVert = 0;\r
451\r
452 // No space-consuming scrollbars.\r
453 if (!scrollbarSize.width) {\r
454 return 0;\r
455 }\r
456 if (hasHeight && height < contentHeight) {\r
457 needVert = 2;\r
458 width -= scrollbarSize.width;\r
459 }\r
460\r
461 if (hasWidth && width < contentWidth) {\r
462 needHorz = 1;\r
463 if (!needVert && hasHeight) {\r
464 height -= scrollbarSize.height;\r
465 if (height < contentHeight) {\r
466 needVert = 2;\r
467 }\r
468 }\r
469 }\r
470\r
471 return needVert + needHorz;\r
472 },\r
473\r
474 /**\r
475 * Returns the owner component's resize element.\r
476 * @return {Ext.dom.Element}\r
477 */\r
478 getTarget: function() {\r
479 return this.owner.getTargetEl();\r
480 },\r
481\r
482 /**\r
483 * @protected\r
484 * Returns all items that are both rendered and visible\r
485 * @return {Array} All matching items\r
486 */\r
487 getVisibleItems: function() {\r
488 var target = this.getRenderTarget(),\r
489 items = this.getLayoutItems(),\r
490 ln = items.length,\r
491 visibleItems = [],\r
492 i, item;\r
493\r
494 for (i = 0; i < ln; i++) {\r
495 item = items[i];\r
496 if (item.rendered && this.isValidParent(item, target, i) && item.hidden !== true && !item.floated) {\r
497 visibleItems.push(item);\r
498 }\r
499 }\r
500\r
501 return visibleItems;\r
502 },\r
503\r
504 getMoveAfterIndex: function (after) {\r
505 return this.owner.items.indexOf(after) + 1;\r
506 },\r
507\r
508 moveItemBefore: function (item, before) {\r
509 var owner = this.owner,\r
510 items = owner.items,\r
511 index = items.indexOf(item),\r
512 toIndex;\r
513\r
514 if (item === before) {\r
515 return item;\r
516 }\r
517\r
518 if (before) {\r
519 toIndex = items.indexOf(before);\r
520 if (index > -1 && index < toIndex) {\r
521 --toIndex;\r
522 }\r
523 } else {\r
524 toIndex = items.length;\r
525 }\r
526\r
527 return owner.insert(toIndex, item);\r
528 },\r
529\r
530 setupRenderTpl: function (renderTpl) {\r
531 renderTpl.renderBody = this.doRenderBody;\r
532 renderTpl.renderContainer = this.doRenderContainer;\r
533 renderTpl.renderItems = this.doRenderItems;\r
534 },\r
535\r
536 getContentTarget: function(){\r
537 return this.owner.getDefaultContentTarget();\r
538 },\r
539\r
540 onAdd: function (item) {\r
541 if (!item.liquidLayout) {\r
542 ++this.activeItemCount;\r
543 }\r
544 this.callParent([item]);\r
545 },\r
546\r
547 onRemove: function(item, isDestroying) {\r
548 if (!item.liquidLayout) {\r
549 --this.activeItemCount;\r
550 }\r
551 this.callParent([item, isDestroying]);\r
552 }\r
553});\r