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