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