When working with tabular data, it is nice to be able to see as many columns as possible. If the table is the only widget on the page, then you can let the table expand and the viewport will scroll. However, this will not work if you need to display multiple tables along with other modules in a more complicated arrangement. Since traditional, fixed width layout is not acceptable, this led to the creation of Page Layout which supports fluid layout of multiple modules on a page.
(Click the screenshot to play with this example.)
Page Layout provides fluid layout for modules arranged either in rows or columns. Each row or column contains one or more modules. Each module contains an optional header, a body, and an optional footer. (A previous incarnation of Page Layout supported arbitrary layouts via nesting, but this turned out to be way too slow.)
You can set the size of each row or column and the size of each module within a horizontal list or vertical stack. Since the layout is fluid, all sizes are specified as percentages of the available width or height. You can also set the minimum supported width and height of your layout (in em’s), to ensure that none of the modules on your page shrink too small.
Modules can be collapsible. A single module in a row (or a module in a vertical stack) collapses vertically, folding up so only the module header is visible. A single module in a column (or a module in a horizontal line) collapses horizontally into a thin bar which can be clicked to re-expand the module.
Individual modules can be a fixed size, managed by CSS. However, this is an edge case, and will not be discussed in this article. Please refer to the examples for details.
Page Layout also supports a page header and page footer which span the width of the page. These are assumed to be fixed height containers.
Page Layout has three modes:
This is the default. The layout is adjusted to fit the viewport. Each module has a vertical scrollbar if the content is taller than the allocated space. The viewport scrollbars only appear if the viewport is smaller than the configured minimum size.
The layout’s width is adjusted to fit the viewport, but each module’s height expands to show all the content. The viewport’s vertical scrollbar appears if the layout is taller than the viewport.
This mode automatically takes effect if there is only one module on the page. Fit to content
is used if the content is not as tall as the viewport. Otherwise, fit to viewport
is used. This is especially useful for a module with controls in the footer, because the controls will stay close to the content instead of being stuck at the bottom of the viewport.
The layout is specified in the markup by attaching classes to the appropriate divs, so you can continue to build the contents of each module the way you normally do.
For a row-based layout, the structure is:
<div class="layout-hd">...</div> <div class="layout-bd" style="visibility:hidden;"> <div class="layout-module-row"> <div class="layout-module"> <div class="layout-m-hd">...</div> <div class="layout-m-bd">...</div> <div class="layout-m-ft">...</div> </div> </div> </div> <div class="layout-ft" style="visibility:hidden;">...</div>
The page header and footer are optional.
The visibility styles are removed once the layout is ready to be displayed. This avoids displaying the raw contents while the page loads.
To transpose to a column-based layout, simply change layout-module-row to layout-module-col. Note that you cannot mix rows and columns.
You can stack as many rows or columns as you want, and you can stack as many modules as want inside each row or column. Of course, if you put too much on one page, the modules will be so small that nothing will be visible! (For efficiency, the layout algorithms do not degrade gracefully. Caveat emptor.)
To set the height of a row or a module inside a column add height:N% to the class, where N is a positive number. To set the width of a column or a module inside a row add width:N% to the class.
To change the mode to fit to content,
add FIT_TO_CONTENT to the class of the main body div (layout-bd). If you want to avoid the single module
behavior, add FORCE_FIT to the main body div’s class.
Modules that collapse vertically simply hide the body and footer. The elements for collapsing and expanding both go inside the module header. Here is an example using buttons:
<div class="layout-m-hd"> ... <button class="layout-vert-collapse-nub">↑</button> <button class="layout-vert-expand-nub">↓</button> ... </div>
Modules that collapse horizontally are replaced by a thin strip that serves as the expand button. Here is an example of collapsing left:
<div class="layout-module"> <div class="layout-left-expand-nub"> <div>→</div> </div> <div class="layout-m-hd"> ... <button class="layout-left-collapse-nub">←</button> ... </div> ... </div>
To collapse to the right, replace left
with right
in the above CSS classes. (Page Layout does not actually make any distinction between collapsing left or right, but the classes are useful for styling.)
Modules can be collapsed or expanded programmatically by using the API: collapseModule, expandModule, toggleModule, and moduleIsCollapsed. All four functions require that you pass the div containing the module, i.e., the one that has the class layout-module.
Once you instantiate Page Layout, it immediately begins managing the modules on the page, so you don’t have to do much. Page Layout automatically reflows the layout when the window is resized or the text size is zoomed. However, you must notify Page Layout when an element on the page changes size. Simply call elementResized and pass the element that changed. You must also notify Page Layout if you add or remove modules, by calling rescanBody.
When displaying a widget that manages its own scrollbars, e.g., Y.DataTable, you need to notify the widget when the module is resized. Page Layout fires two events: beforeResizeModule and afterResizeModule. The plugin for DataTable shows how to handle these events. Before the module is resized, reset the height to auto. Afterwards, set the height to fit the module.
April 17, 2012 at 6:15 pm
Nice, I’m still using YUI2 Layout Manager in a big project that I recently migrated to YUI3.
Definitely going to have to try and play with this some time.
April 19, 2012 at 3:41 am
That’s really cool, thanks for sharing!
Steven
April 21, 2012 at 3:18 am
Fantastic
Now to complete the picture, if only the columns could support resize…
April 23, 2012 at 9:25 am
If you set an element (row/column/module) to be fixed size, then you could attach YUI Resize to the element and then call PageLayout.elementResized() during the drag operation.
September 29, 2012 at 4:14 pm
Page layout always looks children from body tag. Is there anyway to configure it so it always do page layout from given node ?
September 29, 2012 at 5:01 pm
Since PageLayout takes control of the entire page, there isn’t really any point in adding extra layers of DOM. Are you trying to use PageLayout for only part of the page?
September 29, 2012 at 9:34 pm
I have mojito application which gives default view a div.
and I have to insert my mark up inside those divs.
I am also using Mojito parent child hierarchy for templating.
September 30, 2012 at 1:46 am
Well I have mojito app where I am using parent child relationship for templates. Mojito gives default view which is a div and I want to put PageLayout inside that div.
September 30, 2012 at 8:14 am
I don’t think Mojito forces you to be inside a div, but to avoid it, I think you would have to maintain your own version of HTMLFrameMojit.
I added a “body” configuration option to PageLayout, so you can specify the element which contains the layout-(hd|bd|ft) elements. This enhancement will be in the next gallery CDN push.
September 30, 2012 at 9:47 am
Thanks John!
March 7, 2013 at 7:44 am
i’m a newbie to YUI and have been trying to add resize functionality you mentioned
>>If you set an element (row/column/module) to be fixed size, then you could attach YUI Resize to the element and then call PageLayout.elementResized() during the drag operation.
I have a simple Layout
Header
Left | Right
Footer
Layout is based on your demo code…
I am a fixed module header
I am a fixed-width, fluid-height module body that scrolls if needed
I am a fixed module header
I am a fluid-width, fixed-height module body that scrolls if needed
Javascript code…
YUI({
gallery: ‘gallery-2013.01.16-21-05′
}).use(‘gallery-layout’,'resize’, function(Y) {
“use strict”;
var resize = new Y.Resize({
node: ‘#left’,
handles: ‘r’
});
var pl = new Y.PageLayout();
resize.on(‘resize:resize’, function(event) {
pl.elementResized();
})
});
The handle appears to the right of the screen, not between the 2 panes as I would have expected.
Can you help me detect what i’m doing wrong ?
Andy
March 11, 2013 at 10:59 am
Can you put your code on jsfiddle.net? It’s possible that Y.Resize is confused because PageLayout uses floating to help position the modules.
March 13, 2013 at 3:52 pm
Sorry for the delay in getting this to you
http://jsfiddle.net/Ta7JZ/
March 13, 2013 at 4:41 pm
The resize handle has position:absolute and right:0, and somehow this causes it to appear at the right edge of the viewport instead of the right edge of the element you are trying to resize. Unfortunately, I can’t figure out why.