1 // ==========================================================================
  2 // Project:   SproutCore
  3 // Copyright: @2013 7x7 Software, Inc.
  4 // License:   Licensed under MIT license (see license.js)
  5 // ==========================================================================
  6 sc_require('views/view');
  7 
  8 /** @private Shared stack plugin (may be horizontal or vertical). */
  9 function _SC_VIEW_STACK_PLUGIN (direction) {
 10   this.direction = direction;
 11 }
 12 
 13 /** @private Properties to observe on child views that affect the overall child view layout. */
 14 _SC_VIEW_STACK_PLUGIN.prototype.childLayoutProperties = ['marginBefore', 'marginAfter', 'isVisible'];
 15 
 16 /** @private When resizeToFit is false, then we need to know when the view's frame changes. */
 17 _SC_VIEW_STACK_PLUGIN.prototype.layoutDependsOnSize = function (view) {
 18   var options = view.get('childViewLayoutOptions');
 19 
 20   if (options) {
 21     return SC.none(options.resizeToFit) ? false : !options.resizeToFit;
 22   } else {
 23     return false;
 24   }
 25 };
 26 
 27 /** @private */
 28 _SC_VIEW_STACK_PLUGIN.prototype.layoutChildViews = function (view) {
 29   var childViews = view.get('childViews'),
 30     options = view.get('childViewLayoutOptions') || {},
 31     resizeToFit = SC.none(options.resizeToFit) ? true : options.resizeToFit,
 32     lastMargin = 0, // Used to avoid adding spacing to the final margin.
 33     marginAfter = options.paddingBefore || 0,
 34     paddingAfter = options.paddingAfter || 0,
 35     firstPosition = 0,
 36     provisionedSpace = 0,
 37     autoFillAvailableSpace = 0,
 38     totalAvailableSpace = 0,
 39     totalFillAvailableSpaceRatio = 0,
 40     spacing = options.spacing || 0,
 41     sizeDimension = this.direction === 'vertical' ? 'height' : 'width',
 42     minSizeDimension = this.direction === 'vertical' ? 'minHeight' : 'minWidth',
 43     startDimension =  this.direction === 'vertical' ? 'top' : 'left',
 44     childView,
 45     fillRatio,
 46     layout,
 47     marginBefore,
 48     i, len;
 49 
 50   // if the view is not configured to resize to fit content, then we give a chance to the children to fill the available space
 51   // we make a 1st pass to check the conditions, to evaluate the available space and the proportions between children
 52   if (!resizeToFit) {
 53 
 54     totalAvailableSpace = view.get('frame')[sizeDimension];
 55 
 56     // if the view is not configured to resize and it doesn't have yet a height/width, it doesn't make sense to layout children
 57     if (!totalAvailableSpace) {
 58       return;
 59     }
 60 
 61     for (i = 0, len = childViews.get('length'); i < len; i++) {
 62       childView = childViews.objectAt(i);
 63 
 64       // Ignore child views with useAbsoluteLayout true, useStaticLayout true or that are not visible.
 65       if (!childView.get('isVisible') ||
 66         childView.get('useAbsoluteLayout') ||
 67         childView.get('useStaticLayout')) {
 68         continue;
 69       }
 70 
 71       layout = childView.get('layout');
 72 
 73       // Determine the top/left margin.
 74       marginBefore = childView.get('marginBefore') || 0;
 75       provisionedSpace += Math.max(marginAfter, marginBefore);
 76 
 77       // if the height/width is not set, let's check if is possible to resize the view
 78       if (SC.none(layout[sizeDimension])) {
 79         fillRatio = childView.get('fillRatio');
 80 
 81         if (!SC.none(fillRatio)) {
 82           totalFillAvailableSpaceRatio += fillRatio;
 83         } else {
 84           // if none of the child views has fillRatio defined, allow the last one to stretch and fill the available space.
 85           if (i === len - 1 && totalFillAvailableSpaceRatio === 0) {
 86             totalFillAvailableSpaceRatio = 1;
 87           }
 88           //@if(debug)
 89           // Add some developer support.
 90           else {
 91             // even if we don't have a height/width set, as last instance we accept the presence of minHeight/minWidth
 92             if (SC.none(layout[minSizeDimension]))
 93             {
 94               if (this.direction === 'vertical') {
 95                 SC.warn('Developer Warning: The SC.View.VERTICAL_STACK plugin requires that each childView layout contains at least a height or has a configured fillRatio. The layout may also optionally contain left and right, left and width, right and width or centerX and width. The childView %@ has an invalid layout/fillRatio: %@'.fmt(childView, SC.stringFromLayout(layout)));
 96               } else {
 97                 SC.warn('Developer Warning: The SC.View.HORIZONTAL_STACK plugin requires that each childView layout contains at least a width or has a configured fillRatio. The layout may also optionally contain top and bottom, top and height, bottom and height or centerY and height. The childView %@ has an invalid layout/fillRatio: %@'.fmt(childView, SC.stringFromLayout(layout)));
 98               }
 99               return;
100             }
101           }
102           //@endif
103         }
104       } else {
105         provisionedSpace += childView.get('borderFrame')[sizeDimension];
106       }
107 
108       // Determine the bottom/right margin.
109       lastMargin = childView.get('marginAfter') || 0;
110       marginAfter = lastMargin || spacing;
111     }
112 
113     // consider the end padding when calculating the provisionedSpace
114     if (provisionedSpace !== 0 || totalFillAvailableSpaceRatio !== 0) {
115       provisionedSpace += Math.max(lastMargin, paddingAfter);
116     }
117 
118     autoFillAvailableSpace = Math.max(0, totalAvailableSpace - provisionedSpace);
119   }
120 
121   // reset the references for the effective layout
122   lastMargin = 0;
123   marginAfter = options.paddingBefore || 0;
124   paddingAfter = options.paddingAfter || 0;
125 
126   for (i = 0, len = childViews.get('length'); i < len; i++) {
127     var size,
128       adjustStart,
129       adjustEnd;
130 
131     childView = childViews.objectAt(i);
132 
133     // Ignore child views with useAbsoluteLayout true, useStaticLayout true or that are not visible.
134     if (!childView.get('isVisible') ||
135       childView.get('useAbsoluteLayout') ||
136       childView.get('useStaticLayout')) {
137       continue;
138     }
139 
140     layout = childView.get('layout');
141 
142     //@if(debug)
143     // Add some developer support. The case of !resizeToFit was already checked above
144     if (resizeToFit && SC.none(layout[sizeDimension]) && SC.none(layout[minSizeDimension])) {
145       if (this.direction === 'vertical') {
146         SC.warn('Developer Warning: The SC.View.VERTICAL_STACK plugin, when configured with resizeToFit, requires that each childView layout contains at least a height/minHeight and optionally also left and right, left and width, right and width or centerX and width.  The childView %@ has an invalid layout: %@'.fmt(childView, SC.stringFromLayout(layout)));
147       } else {
148         SC.warn('Developer Warning: The SC.View.HORIZONTAL_STACK plugin, when configured with resizeToFit, requires that each childView layout contains at least a width/minWidth and optionally also top and bottom, top and height, bottom and height or centerY and height.  The childView %@ has an invalid layout: %@'.fmt(childView, SC.stringFromLayout(layout)));
149       }
150       return;
151     }
152     //@endif
153 
154     // Determine the top/left margin.
155     marginBefore = childView.get('marginBefore') || 0;
156     firstPosition += Math.max(marginAfter, marginBefore);
157 
158     // Try to avoid useless adjustments to top/left or bottom/right or top/left then bottom/right.
159     // The required adjustments will be merged into a single call
160     adjustStart = layout[startDimension] !== firstPosition;
161 
162     childView.beginPropertyChanges();
163     if (!resizeToFit && !layout[sizeDimension]) {
164       var endDimension = this.direction === 'vertical' ? 'bottom' : 'right',
165           endPosition;
166 
167       fillRatio = childView.get('fillRatio');
168 
169       // if the last child doesn't define fillRatio, default it to 1 as above during the 1st pass
170       if (i === len - 1 && SC.none(fillRatio)) {
171         fillRatio = 1;
172       }
173 
174       // we should get here only in two cases: 1. child defines fillRatio, 2. child defines a minHeight
175       // if both defined, we prefer to handle fillRatio, the other case being handled below by the normal adjustment to top/left
176       if (!SC.none(fillRatio)) {
177         var currentAvailableSpaceRatio = (fillRatio / totalFillAvailableSpaceRatio);
178 
179         // calculate the height/width according to fillRatio and totalFillAvailableSpaceRatio
180         // but set the bottom/right position so any subsequent layout is not considering the height/width as fixed
181         size = Math.ceil(autoFillAvailableSpace * currentAvailableSpaceRatio);
182 
183         // INCOMPLETE: We need to flag this view as constrained and re-compute all the auto-fill amounts
184         // Constrain the height/width to the maximum height/width allowed.
185         // var maxHeight = layout.maxHeight;
186         // if (!SC.none(maxHeight)) {
187         //   // Constrain the height/width according to maxHeight/maxWidth. Which frees up additional available space for further child views.
188         //   if (size > maxSize) {
189         //     size = maxSize;
190         //   }
191         // }
192 
193         // Determine the bottom/right position. If the position overflows (i.e. goes negative) because of rounding up, stop at 0.
194         endPosition = Math.max(0, totalAvailableSpace - firstPosition - size);
195         adjustEnd = layout[endDimension] !== endPosition;
196 
197         if (adjustEnd) {
198           childView.adjust(endDimension, endPosition);
199         }
200       }
201     }
202 
203     if (adjustStart) {
204       childView.adjust(startDimension, firstPosition);
205     }
206     childView.endPropertyChanges();
207 
208     firstPosition += childView.get('borderFrame')[sizeDimension];
209 
210     // Determine the bottom/right margin.
211     lastMargin = childView.get('marginAfter') || 0;
212     marginAfter = lastMargin || spacing;
213   }
214 
215   // If the current size is 0 (all children are hidden), it doesn't make sense to add the padding
216   if (firstPosition !== 0) {
217     firstPosition += Math.max(lastMargin, paddingAfter);
218   }
219 
220   // Adjust our frame to fit as well, this ensures that scrolling works.
221   if (resizeToFit && view.get('layout')[sizeDimension] !== firstPosition) {
222     view.adjust(sizeDimension, firstPosition);
223   }
224 };
225 
226 
227 SC.mixin(SC.View,
228   /** @scope SC.View */ {
229 
230   /**
231     This child layout plugin automatically positions the view's child views in a
232     horizontal stack and optionally adjusts the view's width to fit. It does this
233     by checking the width of each child view and positioning the following child
234     views accordingly. Afterwards, by default, any time that a child view's
235     width or visibility changes, the view will use this plugin to re-adjust all
236     following child views' positions and potentially its own width appropriately.
237 
238     This allows you to stack absolutely positioned views that dynamically change
239     their width and/or visibility without having to resort to using browser
240     flow layout.
241 
242     For example,
243 
244         MyApp.MyView = SC.View.extend({
245 
246           // Child views will be stacked in order horizontally.
247           childViewLayout: SC.View.HORIZONTAL_STACK,
248 
249           // The order of child views is important!
250           childViews: ['sectionA', 'sectionB', 'sectionC'],
251 
252           // The view will resize itself to fit its children.
253           // i.e. We don't need to specify layout.width, this is automatic.
254           // The actual layout will become { left: 10, bottom: 20, top: 20, width: 270 } initially.
255           layout: { left: 10, bottom: 20, top: 20 },
256 
257           sectionA: SC.View.design({
258 
259             // We don't need to specify layout.left, this is automatic.
260             // The actual layout will become { left: 0, bottom: 0, top: 0, width: 100 } initially.
261             layout: { width: 100 }
262 
263           }),
264 
265           sectionB: SC.View.design({
266 
267             // We don't need to specify layout.left, this is automatic.
268             // The actual layout will become { border: 1, left: 100, bottom: 0, top: 0, width: 50 } initially.
269             layout: { border: 1, width: 50 }
270 
271           }),
272 
273           sectionC: SC.View.design({
274 
275             // We don't need to specify layout.left, this is automatic.
276             // The actual layout will become { left: 150, bottom: 10, top: 10, width: 120 } initially.
277             layout: { right: 10, top: 10, width: 120 }
278 
279           })
280 
281         });
282 
283     ## Modify the default behavior with `childViewLayoutOptions`
284 
285     To modify the plugin behavior for all child view layouts, you can set the
286     following child view layout options in `childViewLayoutOptions` on the view:
287 
288       - paddingBefore - Adds padding before the first child view.  Default: 0
289       - paddingAfter - Adds padding after the last child view.  Default: 0
290       - spacing - Adds spacing between each child view.  Default: 0
291       - resizeToFit - Whether to resize the view to fit the child views (requires that each child view has a layout width).  Default: true
292 
293     For example,
294 
295         MyApp.MyView = SC.View.extend({
296 
297           // Child views will be stacked in order horizontally.
298           childViewLayout: SC.View.HORIZONTAL_STACK,
299 
300           // Change the behavior of the HORIZONTAL_STACK plugin
301           childViewLayoutOptions: {
302             paddingBefore: 10,
303             paddingAfter: 20,
304             spacing: 5
305           },
306 
307           // The order of child views is important!
308           childViews: ['sectionA', 'sectionB', 'sectionC'],
309 
310           // The view will resize itself to fit its children. The actual layout will become { left: 10, bottom: 20, top: 20, width: 310 }
311           layout: { left: 10, bottom: 20, top: 20 }, // Don't need to specify layout.width, this is automatic.
312 
313           sectionA: SC.View.design({
314 
315             // Actual layout will become { left: 10, bottom: 0, top: 0, width: 100 }
316             layout: { width: 100 } // Don't need to specify layout.left, this is automatic.
317 
318           }),
319 
320           sectionB: SC.View.design({
321 
322             // Actual layout will become { border: 1, left: 115, bottom: 0, top: 0, width: 50 }
323             layout: { border: 1, width: 50 } // Don't need to specify layout.left, this is automatic.
324 
325           }),
326 
327           sectionC: SC.View.design({
328 
329             // Actual layout will become { left: 170, top: 10, bottom: 10, width: 120 }
330             layout: { top: 10, bottom: 10, width: 120 } // Don't need to specify layout.left, this is automatic.
331 
332           })
333 
334         });
335 
336     If `resizeToFit` is set to `false`, the view will not adjust itself to fit
337     its child views.  This means that when `resizeToFit` is false, the view should
338     specify its width component in its layout. A direct effect of this is the
339     possibility for the child views to automatically expand or shrink in order to
340     fill the empty, unclaimed space of the view.
341 
342     This available space is shared between all children that don't specify a fixed width
343     such that their final width is calculated proportionally to the value of the
344     property `fillRatio`.
345 
346     For simplicity, when none of the children specifies `fillRatio`,
347     you can ignore the last child view's layout width and the last child view
348     will stretch to fill the parent view.
349 
350     For example,
351 
352         MyApp.MyView = SC.View.extend({
353 
354           // Child views will be stacked in order horizontally.
355           childViewLayout: SC.View.HORIZONTAL_STACK,
356 
357           // Change the behavior of the HORIZONTAL_STACK plugin
358           childViewLayoutOptions: {
359             paddingBefore: 10,
360             paddingAfter: 20,
361             spacing: 5,
362             resizeToFit: false // Setting this to false, so that the child views stretch/contract to fit the parent's size.
363           },
364 
365           // The order of child views is important!
366           childViews: ['sectionA', 'sectionB', 'sectionC'],
367 
368           // The parent view will not resize itself to fit its contents, so we specify the width.
369           layout: { left: 10, bottom: 20, top: 20, width: 500 },
370 
371           sectionA: SC.View.design({
372 
373             // We don't need to specify layout.left, this is automatic. This child will not stretch, its width is set.
374             // Actual layout will become { left: 10, bottom: 0, top: 0, width: 100 }
375             layout: { width: 100 }
376 
377           }),
378 
379           sectionB: SC.View.design({
380 
381             // The unclaimed space so far is 500 - 10 - 100 - 5 - 5 - 20, or 360px. This space will be shared between
382             // the two last sections, because we won't specity a width on them.
383             // This view will get 1/3 of the available space, because the other flexibile view has a ratio of 2.
384             fillRatio: 1,
385 
386             // This section will take 1/3 * 360px = 120px.
387             // Actual layout will become { border: 1, left: 115, bottom: 0, top: 0, right: 265 }, in other words, width == 120
388             // We don't need to specify layout.left, layout.right or layout.width, this is automatic.
389             layout: { border: 1 }
390 
391           }),
392 
393           sectionC: SC.View.design({
394 
395             // This view will get 2/3 of the available space, because the other flexibile view has a ratio of 1.
396             fillRatio: 2,
397 
398             // This section will take 2/3 * 360px = 240px.
399             // Actual layout will become { left: 240, top: 10, bottom: 10, right: 20 }, in other words, width == 240
400             // We don't need to specify layout.left, layout.right or layout.width, this is automatic.
401             layout: { top: 10, bottom: 10 }
402 
403           })
404 
405         });
406 
407     ## Modify specific child view layouts
408 
409     To adjust the child layout on a granular level per child view, you can
410     also set the following properties on each child view:
411 
412       - marginBefore - Specify the minimum spacing above the child view.
413       - marginAfter - Specify the minimum spacing below the child view.
414       - useAbsoluteLayout - Don't include this child view in automatic layout, use absolute positioning based on the child view's `layout` property.
415       - useStaticLayout - Don't include this child view in automatic layout.  This child view uses relative positioning and is not eligible for automatic layout.
416       - isVisible - Non-visible child views are not included in the stack.
417       - fillRatio - When the parent view is configured with a fixed dimension, children not specifying a width but specifying fillRatio will be resized to fill the unclaimed space proportionally to this ratio.
418 
419       For example,
420 
421         MyApp.MyView = SC.View.extend({
422 
423           // Child views will be stacked in order horizontally.
424           childViewLayout: SC.View.HORIZONTAL_STACK,
425 
426           // Actual layout will become { left: 10, right: 10, top: 20, width: 570 }
427           layout: { left: 10, right: 10, top: 20 },
428 
429           // Keep the child views ordered!
430           childViews: ['sectionA', 'float', 'sectionB', 'sectionC'],
431 
432           sectionA: SC.View.design({
433             // Actual layout will become { left: 0, right: 50, top: 0, width: 100 }
434             layout: { right: 50, width: 100 },
435             // The following child view will be at least 50px further right.
436             marginAfter: 50
437           }),
438 
439           float: SC.View.design({
440             // This view will not be included in automatic layout and will not effect the stack.
441             layout: { top: 5, right: 5, height: 50, width: 50 },
442             useAbsoluteLayout: true
443           }),
444 
445           sectionB: SC.View.design({
446             // Actual layout will become { left: 1500, right: 0, top: 0, width: 120 }
447             layout: { width: 120 }
448           }),
449 
450           sectionC: SC.View.design({
451             // Actual layout will become { left: 470, bottom: 0, top: 0, width: 100 }
452             layout: { width: 100 },
453             // This child view will be at least 200px to the right of the previous.
454             marginBefore: 200
455           })
456 
457         });
458 
459     ### A Note About Spacing
460 
461     Note that the spacing attribute in `childViewLayoutOptions` becomes the
462     _minimum margin between child views, without explicitly overriding it from
463     both sides using `marginAfter` and `marginBefore`_.  For example, if `spacing`
464     is 25, setting `marginAfter` to 10 on a child view will not result in the
465     next child view being 10px to the right of it, unless the next child view also
466     specified `marginBefore` as 10.
467 
468     What this means is that it takes less configuration if you set `spacing` to
469     be the _smallest margin you wish to exist between child views_ and then use
470     the overrides to grow the margin if necessary.  For example, if `spacing`
471     is 5, setting `marginAfter` to 10 on a child view will result in the next
472     child view being 10px to the right of it, without having to also specify
473     `marginBefore` on that next child view.
474 
475     @extends SC.ChildViewLayoutProtocol
476     @since Version 1.10
477   */
478   HORIZONTAL_STACK: new _SC_VIEW_STACK_PLUGIN('horizontal'),
479 
480   /**
481     This child layout plugin automatically positions the view's child views in a
482     vertical stack and optionally adjusts the view's height to fit.  It does this
483     by checking the height of each child view and positioning the following child
484     view accordingly.  Afterwards, by default, any time that a child view's
485     height or visibility changes, the view will use this plugin to re-adjust all
486     following child views' positions and potentially its own height appropriately.
487 
488     This allows you to stack absolutely positioned views that dynamically change
489     their height and/or visibility without having to resort to using browser
490     flow layout.
491 
492     A typical usage scenario is a long "form" made of multiple subsection
493     views.  If we want to adjust the height of a subsection, to make space for
494     an error label for example, it would be a lot of work to manually
495     reposition all the following sections below it.  A much easier to code and
496     cleaner solution is to just set the childViewLayout plugin on the wrapper
497     view.
498 
499     For example,
500 
501         MyApp.MyView = SC.View.extend({
502 
503           // Child views will be stacked in order vertically.
504           childViewLayout: SC.View.VERTICAL_STACK,
505 
506           // The order of child views is important!
507           childViews: ['sectionA', 'sectionB', 'sectionC'],
508 
509           // The view will resize itself to fit its children.
510           // i.e. We don't need to specify layout.height, this is automatic.
511           // The actual layout will become { left: 10, right: 10, top: 20, height: 270 } initially.
512           layout: { left: 10, right: 10, top: 20 },
513 
514           sectionA: SC.View.design({
515 
516             // We don't need to specify layout.top, this is automatic.
517             // The actual layout will become { left: 0, right: 0, top: 0, height: 100 } initially.
518             layout: { height: 100 }
519 
520           }),
521 
522           sectionB: SC.View.design({
523 
524             // We don't need to specify layout.top, this is automatic.
525             // The actual layout will become { border: 1, left: 0, right: 0, top: 100, height: 50 } initially.
526             layout: { border: 1, height: 50 }
527 
528           }),
529 
530           sectionC: SC.View.design({
531 
532             // We don't need to specify layout.top, this is automatic.
533             // The actual layout will become { left: 10, right: 10, top: 150, height: 120 } initially.
534             layout: { left: 10, right: 10, height: 120 }
535 
536           })
537 
538         });
539 
540     ## Modify the default behavior with `childViewLayoutOptions`
541 
542     To modify the plugin behavior for all child view layouts, you can set the
543     following child view layout options in `childViewLayoutOptions` on the view:
544 
545       - paddingBefore - Adds padding before the first child view.  Default: 0
546       - paddingAfter - Adds padding after the last child view.  Default: 0
547       - spacing - Adds spacing between each child view.  Default: 0
548       - resizeToFit - Whether to resize the view to fit the child views (requires that each child view has a layout height).  Default: true
549 
550     For example,
551 
552         MyApp.MyView = SC.View.extend({
553 
554           // Child views will be stacked in order vertically.
555           childViewLayout: SC.View.VERTICAL_STACK,
556 
557           // Change the behavior of the VERTICAL_STACK plugin
558           childViewLayoutOptions: {
559             paddingBefore: 10,
560             paddingAfter: 20,
561             spacing: 5
562           },
563 
564           // The order of child views is important!
565           childViews: ['sectionA', 'sectionB', 'sectionC'],
566 
567           // The actual layout will become { left: 10, right: 10, top: 20, height: 310 } initially.
568           layout: { left: 10, right: 10, top: 20 }, // Don't need to specify layout.height, this is automatic.
569 
570           sectionA: SC.View.design({
571 
572             // We don't need to specify layout.top, this is automatic.
573             // The actual layout will become { left: 0, right: 0, top: 10, height: 100 } initially.
574             layout: { height: 100 }
575 
576           }),
577 
578           sectionB: SC.View.design({
579 
580             // We don't need to specify layout.top, this is automatic.
581             // The actual layout will become { border: 1, left: 0, right: 0, top: 115, height: 50 } initially.
582             layout: { border: 1, height: 50 }
583 
584           }),
585 
586           sectionC: SC.View.design({
587 
588             // We don't need to specify layout.top, this is automatic.
589             // The actual layout will become { left: 10, right: 10, top: 170, height: 120 } initially.
590             layout: { left: 10, right: 10, height: 120 }
591 
592           })
593 
594         });
595 
596     If `resizeToFit` is set to `false`, the view will not adjust itself to fit
597     its child views.  This means that when `resizeToFit` is false, the view should
598     specify its height component in its layout. A direct effect is the possibility for
599     the child views to automatically extend or shrink in order to fill the empty, unclaimed space.
600     This available space is shared between the children not specifying a fixed height
601     and their final dimension is calculated proportionally to the value of the
602     property `fillRatio`.
603     For simplicity, when none of the children specifies `fillRatio`,
604     you can ignore the last child view's layout height if you want the last child view
605     to stretch to fill the parent view.
606 
607     For example,
608 
609         MyApp.MyView = SC.View.extend({
610 
611           // Child views will be stacked in order vertically.
612           childViewLayout: SC.View.VERTICAL_STACK,
613 
614           // Change the behavior of the VERTICAL_STACK plugin
615           childViewLayoutOptions: {
616             paddingBefore: 10,
617             paddingAfter: 20,
618             spacing: 5,
619             resizeToFit: false
620           },
621 
622           // The order of child views is important!
623           childViews: ['sectionA', 'sectionB', 'sectionC'],
624 
625           // Actual layout will become { left: 10, right: 10, top: 20, height: 500 }
626           layout: { left: 10, right: 10, top: 20, height: 500 }, // Need to specify layout.height.
627 
628           sectionA: SC.View.design({
629 
630             // We don't need to specify layout.top, this is automatic. This child will not stretch, its height is set.
631             // The actual layout will become { left: 0, right: 0, top: 10, height: 100 } initially.
632             layout: { height: 100 }
633 
634           }),
635 
636           sectionB: SC.View.design({
637 
638             // The unclaimed space so far is 500 - 10 - 100 - 5 - 5 - 20, or 360px. This space will be shared between
639             // the two last sections, because we won't specity a height on them.
640             // This view will get 1/3 of the available space, because the other flexibile view has a ratio of 2.
641             fillRatio: 1,
642 
643             // This section will take 1/3 * 360px = 120px.
644             // Actual layout will become { border: 1, left: 0, right: 0, top: 115, bottom: 265 }, in other words, height == 120
645             // We don't need to specify layout.top, layout.bottom or layout.height, this is automatic.
646             layout: { border: 1 }
647 
648           }),
649 
650           sectionC: SC.View.design({
651 
652             // This view will get 2/3 of the available space, because the other flexibile view has a ratio of 1.
653             fillRatio: 2,
654 
655             // This section will take 2/3 * 360px = 240px.
656             // Actual layout will become { left: 10, right: 10, top: 240, bottom: 20 }, in other words, height == 240
657             // We don't need to specify layout.top, layout.bottom or layout.height, this is automatic.
658             layout: { left: 10, right: 10 }
659 
660           })
661 
662         });
663 
664     ## Modify specific child view layouts
665 
666     To adjust the child layout on a granular level per child view, you can
667     also set the following properties on each child view:
668 
669       - marginBefore - Specify the minimum spacing above the child view.
670       - marginAfter - Specify the minimum spacing below the child view.
671       - useAbsoluteLayout - Don't include this child view in automatic layout, use absolute positioning based on the child view's `layout` property.
672       - useStaticLayout - Don't include this child view in automatic layout.  This child view uses relative positioning and is not eligible for automatic layout.
673       - isVisible - Non-visible child views are not included in the stack.
674       - fillRatio - When the parent view is configured with a fixed dimension, children not specifying a height but specifying fillRatio will be resized to fill the unclaimed space proportionally to this ratio.
675 
676     For example,
677 
678         MyApp.MyView = SC.View.extend({
679 
680           // Child views will be stacked in order vertically.
681           childViewLayout: SC.View.VERTICAL_STACK,
682 
683           // Actual layout will become { left: 10, right: 10, top: 20, height: 570 }
684           layout: { left: 10, right: 10, top: 20 },
685 
686           // Keep the child views ordered!
687           childViews: ['sectionA', 'float', 'sectionB', 'sectionC'],
688 
689           sectionA: SC.View.design({
690             // Actual layout will become { left: 0, right: 50, top: 0, height: 100 }
691             layout: { right: 50, height: 100 },
692             // The following child view will be at least 50px further down.
693             marginAfter: 50
694           }),
695 
696           float: SC.View.design({
697             // This view will not be included in automatic layout and will not effect the stack.
698             layout: { top: 5, right: 5, width: 50, height: 50 },
699             useAbsoluteLayout: true
700           }),
701 
702           sectionB: SC.View.design({
703             // Actual layout will become { left: 0, right: 0, top: 150, height: 120 }
704             layout: { height: 120 }
705           }),
706 
707           sectionC: SC.View.design({
708             // Actual layout will become { left: 0, bottom: 0, top: 470, height: 100 }
709             layout: { height: 100 },
710             // This child view will be at least 200px below the previous.
711             marginBefore: 200
712           })
713 
714         });
715 
716     ### A Note About Spacing
717 
718     Note that the spacing attribute in `childViewLayoutOptions` becomes the
719     _minimum margin between child views, without explicitly overriding it from
720     both sides using `marginAfter` and `marginBefore`_.  For example, if `spacing`
721     is 25, setting `marginAfter` to 10 on a child view will not result in the
722     next child view being 10px below it, unless the next child view also
723     specified `marginBefore` as 10.
724 
725     What this means is that it takes less configuration if you set `spacing` to
726     be the _smallest margin you wish to exist between child views_ and then use
727     the overrides to grow the margin if necessary.  For example, if `spacing`
728     is 5, setting `marginAfter` to 10 on a child view will result in the next
729     child view being 10px below it, without having to also specify `marginBefore`
730     on that next child view.
731 
732     @extends SC.ChildViewLayoutProtocol
733     @since Version 1.10
734   */
735   VERTICAL_STACK: new _SC_VIEW_STACK_PLUGIN('vertical')
736 
737 });
738