PageRenderTime 44ms CodeModel.GetById 31ms app.highlight 9ms RepoModel.GetById 0ms app.codeStats 1ms

/ext-4.0.7/src/layout/container/Table.js

https://bitbucket.org/srogerf/javascript
JavaScript | 308 lines | 129 code | 32 blank | 147 comment | 31 complexity | 3197a647ba38cda05cb3482ed006a340 MD5 | raw file
  1/*
  2
  3This file is part of Ext JS 4
  4
  5Copyright (c) 2011 Sencha Inc
  6
  7Contact:  http://www.sencha.com/contact
  8
  9GNU General Public License Usage
 10This 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.
 11
 12If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
 13
 14*/
 15/**
 16 * This layout allows you to easily render content into an HTML table. The total number of columns can be specified, and
 17 * rowspan and colspan can be used to create complex layouts within the table. This class is intended to be extended or
 18 * created via the `layout: {type: 'table'}` {@link Ext.container.Container#layout} config, and should generally not
 19 * need to be created directly via the new keyword.
 20 *
 21 * Note that when creating a layout via config, the layout-specific config properties must be passed in via the {@link
 22 * Ext.container.Container#layout} object which will then be applied internally to the layout. In the case of
 23 * TableLayout, the only valid layout config properties are {@link #columns} and {@link #tableAttrs}. However, the items
 24 * added to a TableLayout can supply the following table-specific config properties:
 25 *
 26 *   - **rowspan** Applied to the table cell containing the item.
 27 *   - **colspan** Applied to the table cell containing the item.
 28 *   - **cellId** An id applied to the table cell containing the item.
 29 *   - **cellCls** A CSS class name added to the table cell containing the item.
 30 *
 31 * The basic concept of building up a TableLayout is conceptually very similar to building up a standard HTML table. You
 32 * simply add each panel (or "cell") that you want to include along with any span attributes specified as the special
 33 * config properties of rowspan and colspan which work exactly like their HTML counterparts. Rather than explicitly
 34 * creating and nesting rows and columns as you would in HTML, you simply specify the total column count in the
 35 * layoutConfig and start adding panels in their natural order from left to right, top to bottom. The layout will
 36 * automatically figure out, based on the column count, rowspans and colspans, how to position each panel within the
 37 * table. Just like with HTML tables, your rowspans and colspans must add up correctly in your overall layout or you'll
 38 * end up with missing and/or extra cells! Example usage:
 39 *
 40 *     @example
 41 *     Ext.create('Ext.panel.Panel', {
 42 *         title: 'Table Layout',
 43 *         width: 300,
 44 *         height: 150,
 45 *         layout: {
 46 *             type: 'table',
 47 *             // The total column count must be specified here
 48 *             columns: 3
 49 *         },
 50 *         defaults: {
 51 *             // applied to each contained panel
 52 *             bodyStyle: 'padding:20px'
 53 *         },
 54 *         items: [{
 55 *             html: 'Cell A content',
 56 *             rowspan: 2
 57 *         },{
 58 *             html: 'Cell B content',
 59 *             colspan: 2
 60 *         },{
 61 *             html: 'Cell C content',
 62 *             cellCls: 'highlight'
 63 *         },{
 64 *             html: 'Cell D content'
 65 *         }],
 66 *         renderTo: Ext.getBody()
 67 *     });
 68 */
 69Ext.define('Ext.layout.container.Table', {
 70
 71    /* Begin Definitions */
 72
 73    alias: ['layout.table'],
 74    extend: 'Ext.layout.container.Auto',
 75    alternateClassName: 'Ext.layout.TableLayout',
 76
 77    /* End Definitions */
 78
 79    /**
 80     * @cfg {Number} columns
 81     * The total number of columns to create in the table for this layout. If not specified, all Components added to
 82     * this layout will be rendered into a single row using one column per Component.
 83     */
 84
 85    // private
 86    monitorResize:false,
 87
 88    type: 'table',
 89
 90    // Table layout is a self-sizing layout. When an item of for example, a dock layout, the Panel must expand to accommodate
 91    // a table layout. See in particular AbstractDock::onLayout for use of this flag.
 92    autoSize: true,
 93
 94    clearEl: true, // Base class will not create it if already truthy. Not needed in tables.
 95
 96    targetCls: Ext.baseCSSPrefix + 'table-layout-ct',
 97    tableCls: Ext.baseCSSPrefix + 'table-layout',
 98    cellCls: Ext.baseCSSPrefix + 'table-layout-cell',
 99
100    /**
101     * @cfg {Object} tableAttrs
102     * An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification used to
103     * create the layout's `<table>` element. Example:
104     *
105     *     {
106     *         xtype: 'panel',
107     *         layout: {
108     *             type: 'table',
109     *             columns: 3,
110     *             tableAttrs: {
111     *                 style: {
112     *                     width: '100%'
113     *                 }
114     *             }
115     *         }
116     *     }
117     */
118    tableAttrs:null,
119
120    /**
121     * @cfg {Object} trAttrs
122     * An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification used to
123     * create the layout's <tr> elements.
124     */
125
126    /**
127     * @cfg {Object} tdAttrs
128     * An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification used to
129     * create the layout's <td> elements.
130     */
131
132    /**
133     * @private
134     * Iterates over all passed items, ensuring they are rendered in a cell in the proper
135     * location in the table structure.
136     */
137    renderItems: function(items) {
138        var tbody = this.getTable().tBodies[0],
139            rows = tbody.rows,
140            i = 0,
141            len = items.length,
142            cells, curCell, rowIdx, cellIdx, item, trEl, tdEl, itemCt;
143
144        // Calculate the correct cell structure for the current items
145        cells = this.calculateCells(items);
146
147        // Loop over each cell and compare to the current cells in the table, inserting/
148        // removing/moving cells as needed, and making sure each item is rendered into
149        // the correct cell.
150        for (; i < len; i++) {
151            curCell = cells[i];
152            rowIdx = curCell.rowIdx;
153            cellIdx = curCell.cellIdx;
154            item = items[i];
155
156            // If no row present, create and insert one
157            trEl = rows[rowIdx];
158            if (!trEl) {
159                trEl = tbody.insertRow(rowIdx);
160                if (this.trAttrs) {
161                    trEl.set(this.trAttrs);
162                }
163            }
164
165            // If no cell present, create and insert one
166            itemCt = tdEl = Ext.get(trEl.cells[cellIdx] || trEl.insertCell(cellIdx));
167            if (this.needsDivWrap()) { //create wrapper div if needed - see docs below
168                itemCt = tdEl.first() || tdEl.createChild({tag: 'div'});
169                itemCt.setWidth(null);
170            }
171
172            // Render or move the component into the cell
173            if (!item.rendered) {
174                this.renderItem(item, itemCt, 0);
175            }
176            else if (!this.isValidParent(item, itemCt, 0)) {
177                this.moveItem(item, itemCt, 0);
178            }
179
180            // Set the cell properties
181            if (this.tdAttrs) {
182                tdEl.set(this.tdAttrs);
183            }
184            tdEl.set({
185                colSpan: item.colspan || 1,
186                rowSpan: item.rowspan || 1,
187                id: item.cellId || '',
188                cls: this.cellCls + ' ' + (item.cellCls || '')
189            });
190
191            // If at the end of a row, remove any extra cells
192            if (!cells[i + 1] || cells[i + 1].rowIdx !== rowIdx) {
193                cellIdx++;
194                while (trEl.cells[cellIdx]) {
195                    trEl.deleteCell(cellIdx);
196                }
197            }
198        }
199
200        // Delete any extra rows
201        rowIdx++;
202        while (tbody.rows[rowIdx]) {
203            tbody.deleteRow(rowIdx);
204        }
205    },
206
207    afterLayout: function() {
208        this.callParent();
209
210        if (this.needsDivWrap()) {
211            // set wrapper div width to match layed out item - see docs below
212            Ext.Array.forEach(this.getLayoutItems(), function(item) {
213                Ext.fly(item.el.dom.parentNode).setWidth(item.getWidth());
214            });
215        }
216    },
217
218    /**
219     * @private
220     * Determine the row and cell indexes for each component, taking into consideration
221     * the number of columns and each item's configured colspan/rowspan values.
222     * @param {Array} items The layout components
223     * @return {Object[]} List of row and cell indexes for each of the components
224     */
225    calculateCells: function(items) {
226        var cells = [],
227            rowIdx = 0,
228            colIdx = 0,
229            cellIdx = 0,
230            totalCols = this.columns || Infinity,
231            rowspans = [], //rolling list of active rowspans for each column
232            i = 0, j,
233            len = items.length,
234            item;
235
236        for (; i < len; i++) {
237            item = items[i];
238
239            // Find the first available row/col slot not taken up by a spanning cell
240            while (colIdx >= totalCols || rowspans[colIdx] > 0) {
241                if (colIdx >= totalCols) {
242                    // move down to next row
243                    colIdx = 0;
244                    cellIdx = 0;
245                    rowIdx++;
246
247                    // decrement all rowspans
248                    for (j = 0; j < totalCols; j++) {
249                        if (rowspans[j] > 0) {
250                            rowspans[j]--;
251                        }
252                    }
253                } else {
254                    colIdx++;
255                }
256            }
257
258            // Add the cell info to the list
259            cells.push({
260                rowIdx: rowIdx,
261                cellIdx: cellIdx
262            });
263
264            // Increment
265            for (j = item.colspan || 1; j; --j) {
266                rowspans[colIdx] = item.rowspan || 1;
267                ++colIdx;
268            }
269            ++cellIdx;
270        }
271
272        return cells;
273    },
274
275    /**
276     * @private
277     * Return the layout's table element, creating it if necessary.
278     */
279    getTable: function() {
280        var table = this.table;
281        if (!table) {
282            table = this.table = this.getTarget().createChild(
283                Ext.apply({
284                    tag: 'table',
285                    role: 'presentation',
286                    cls: this.tableCls,
287                    cellspacing: 0, //TODO should this be specified or should CSS handle it?
288                    cn: {tag: 'tbody'}
289                }, this.tableAttrs),
290                null, true
291            );
292        }
293        return table;
294    },
295
296    /**
297     * @private
298     * Opera 10.5 has a bug where if a table cell's child has box-sizing:border-box and padding, it
299     * will include that padding in the size of the cell, making it always larger than the
300     * shrink-wrapped size of its contents. To get around this we have to wrap the contents in a div
301     * and then set that div's width to match the item rendered within it afterLayout. This method
302     * determines whether we need the wrapper div; it currently does a straight UA sniff as this bug
303     * seems isolated to just Opera 10.5, but feature detection could be added here if needed.
304     */
305    needsDivWrap: function() {
306        return Ext.isOpera10_5;
307    }
308});