3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.layout.container.Table
17 * @extends Ext.layout.container.Auto
18 * <p>This layout allows you to easily render content into an HTML table. The total number of columns can be
19 * specified, and rowspan and colspan can be used to create complex layouts within the table.
20 * This class is intended to be extended or created via the <code>layout: {type: 'table'}</code>
21 * {@link Ext.container.Container#layout} config, and should generally not need to be created directly via the new keyword.</p>
22 * <p>Note that when creating a layout via config, the layout-specific config properties must be passed in via
23 * the {@link Ext.container.Container#layout} object which will then be applied internally to the layout. In the
24 * case of TableLayout, the only valid layout config properties are {@link #columns} and {@link #tableAttrs}.
25 * However, the items added to a TableLayout can supply the following table-specific config properties:</p>
27 * <li><b>rowspan</b> Applied to the table cell containing the item.</li>
28 * <li><b>colspan</b> Applied to the table cell containing the item.</li>
29 * <li><b>cellId</b> An id applied to the table cell containing the item.</li>
30 * <li><b>cellCls</b> A CSS class name added to the table cell containing the item.</li>
32 * <p>The basic concept of building up a TableLayout is conceptually very similar to building up a standard
33 * HTML table. You simply add each panel (or "cell") that you want to include along with any span attributes
34 * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts.
35 * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the
36 * total column count in the layoutConfig and start adding panels in their natural order from left to right,
37 * top to bottom. The layout will automatically figure out, based on the column count, rowspans and colspans,
38 * how to position each panel within the table. Just like with HTML tables, your rowspans and colspans must add
39 * up correctly in your overall layout or you'll end up with missing and/or extra cells! Example usage:</p>
40 * {@img Ext.layout.container.Table/Ext.layout.container.Table.png Ext.layout.container.Table container layout}
42 // This code will generate a layout table that is 3 columns by 2 rows
43 // with some spanning included. The basic layout will be:
44 // +--------+-----------------+
46 // | |--------+--------|
48 // +--------+--------+--------+
49 Ext.create('Ext.panel.Panel', {
50 title: 'Table Layout',
55 // The total column count must be specified here
59 // applied to each contained panel
60 bodyStyle:'padding:20px'
63 html: 'Cell A content',
66 html: 'Cell B content',
69 html: 'Cell C content',
72 html: 'Cell D content'
74 renderTo: Ext.getBody()
79 Ext.define('Ext.layout.container.Table', {
81 /* Begin Definitions */
83 alias: ['layout.table'],
84 extend: 'Ext.layout.container.Auto',
85 alternateClassName: 'Ext.layout.TableLayout',
90 * @cfg {Number} columns
91 * The total number of columns to create in the table for this layout. If not specified, all Components added to
92 * this layout will be rendered into a single row using one column per Component.
100 // Table layout is a self-sizing layout. When an item of for example, a dock layout, the Panel must expand to accommodate
101 // a table layout. See in particular AbstractDock::onLayout for use of this flag.
104 clearEl: true, // Base class will not create it if already truthy. Not needed in tables.
106 targetCls: Ext.baseCSSPrefix + 'table-layout-ct',
107 tableCls: Ext.baseCSSPrefix + 'table-layout',
108 cellCls: Ext.baseCSSPrefix + 'table-layout-cell',
111 * @cfg {Object} tableAttrs
112 * <p>An object containing properties which are added to the {@link Ext.core.DomHelper DomHelper} specification
113 * used to create the layout's <tt><table></tt> element. Example:</p><pre><code>
130 * @cfg {Object} trAttrs
131 * <p>An object containing properties which are added to the {@link Ext.core.DomHelper DomHelper} specification
132 * used to create the layout's <tt><tr></tt> elements.
136 * @cfg {Object} tdAttrs
137 * <p>An object containing properties which are added to the {@link Ext.core.DomHelper DomHelper} specification
138 * used to create the layout's <tt><td></tt> elements.
143 * Iterates over all passed items, ensuring they are rendered in a cell in the proper
144 * location in the table structure.
146 renderItems: function(items) {
147 var tbody = this.getTable().tBodies[0],
151 cells, curCell, rowIdx, cellIdx, item, trEl, tdEl, itemCt;
153 // Calculate the correct cell structure for the current items
154 cells = this.calculateCells(items);
156 // Loop over each cell and compare to the current cells in the table, inserting/
157 // removing/moving cells as needed, and making sure each item is rendered into
159 for (; i < len; i++) {
161 rowIdx = curCell.rowIdx;
162 cellIdx = curCell.cellIdx;
165 // If no row present, create and insert one
168 trEl = tbody.insertRow(rowIdx);
170 trEl.set(this.trAttrs);
174 // If no cell present, create and insert one
175 itemCt = tdEl = Ext.get(trEl.cells[cellIdx] || trEl.insertCell(cellIdx));
176 if (this.needsDivWrap()) { //create wrapper div if needed - see docs below
177 itemCt = tdEl.first() || tdEl.createChild({tag: 'div'});
178 itemCt.setWidth(null);
181 // Render or move the component into the cell
182 if (!item.rendered) {
183 this.renderItem(item, itemCt, 0);
185 else if (!this.isValidParent(item, itemCt, 0)) {
186 this.moveItem(item, itemCt, 0);
189 // Set the cell properties
191 tdEl.set(this.tdAttrs);
194 colSpan: item.colspan || 1,
195 rowSpan: item.rowspan || 1,
196 id: item.cellId || '',
197 cls: this.cellCls + ' ' + (item.cellCls || '')
200 // If at the end of a row, remove any extra cells
201 if (!cells[i + 1] || cells[i + 1].rowIdx !== rowIdx) {
203 while (trEl.cells[cellIdx]) {
204 trEl.deleteCell(cellIdx);
209 // Delete any extra rows
211 while (tbody.rows[rowIdx]) {
212 tbody.deleteRow(rowIdx);
216 afterLayout: function() {
219 if (this.needsDivWrap()) {
220 // set wrapper div width to match layed out item - see docs below
221 Ext.Array.forEach(this.getLayoutItems(), function(item) {
222 Ext.fly(item.el.dom.parentNode).setWidth(item.getWidth());
229 * Determine the row and cell indexes for each component, taking into consideration
230 * the number of columns and each item's configured colspan/rowspan values.
231 * @param {Array} items The layout components
232 * @return {Array} List of row and cell indexes for each of the components
234 calculateCells: function(items) {
239 totalCols = this.columns || Infinity,
240 rowspans = [], //rolling list of active rowspans for each column
245 for (; i < len; i++) {
248 // Find the first available row/col slot not taken up by a spanning cell
249 while (colIdx >= totalCols || rowspans[colIdx] > 0) {
250 if (colIdx >= totalCols) {
251 // move down to next row
256 // decrement all rowspans
257 for (j = 0; j < totalCols; j++) {
258 if (rowspans[j] > 0) {
267 // Add the cell info to the list
274 for (j = item.colspan || 1; j; --j) {
275 rowspans[colIdx] = item.rowspan || 1;
286 * Return the layout's table element, creating it if necessary.
288 getTable: function() {
289 var table = this.table;
291 table = this.table = this.getTarget().createChild(
294 role: 'presentation',
296 cellspacing: 0, //TODO should this be specified or should CSS handle it?
307 * Opera 10.5 has a bug where if a table cell's child has box-sizing:border-box and padding, it
308 * will include that padding in the size of the cell, making it always larger than the
309 * shrink-wrapped size of its contents. To get around this we have to wrap the contents in a div
310 * and then set that div's width to match the item rendered within it afterLayout. This method
311 * determines whether we need the wrapper div; it currently does a straight UA sniff as this bug
312 * seems isolated to just Opera 10.5, but feature detection could be added here if needed.
314 needsDivWrap: function() {
315 return Ext.isOpera10_5;