X-Git-Url: http://git.roojs.org/?p=roojs1;a=blobdiff_plain;f=Roo%2Fbootstrap%2FTable.js;h=e7ad18308dec1e148347af2e825a8cce12a20067;hp=863e67695a8c410effc0bf803a2a79a4e7d8cfc1;hb=d732873374fc70af4babb054fe8437fb8c42448a;hpb=3693e27308297e9b9d0b1bff71ed191f99e51f08 diff --git a/Roo/bootstrap/Table.js b/Roo/bootstrap/Table.js index 863e67695a..e7ad18308d 100644 --- a/Roo/bootstrap/Table.js +++ b/Roo/bootstrap/Table.js @@ -1,62 +1,118 @@ -/* - * - LGPL - * - * table - * - */ - /** * @class Roo.bootstrap.Table + * @licence LGBL * @extends Roo.bootstrap.Component - * Bootstrap Table class - * @cfg {String} cls table class - * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text - * @cfg {String} bgcolor Specifies the background color for a table - * @cfg {Number} border Specifies whether the table cells should have borders or not - * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content - * @cfg {Number} cellspacing Specifies the space between cells - * @cfg {String} frame Specifies which parts of the outside borders that should be visible - * @cfg {String} rules Specifies which parts of the inside borders that should be visible - * @cfg {String} sortable Specifies that the table should be sortable - * @cfg {String} summary Specifies a summary of the content of a table - * @cfg {Number} width Specifies the width of a table - * @cfg {String} layout table layout (auto | fixed | initial | inherit) + * @children Roo.bootstrap.TableBody + * Bootstrap Table class. This class represents the primary interface of a component based grid control. + * Similar to Roo.grid.Grid + *

+ var table = Roo.factory({
+    xtype : 'Table',
+    xns : Roo.bootstrap,
+    autoSizeColumns: true,
+    
+    
+    store : {
+        xtype : 'Store',
+        xns : Roo.data,
+        remoteSort : true,
+        sortInfo : { direction : 'ASC', field: 'name' },
+        proxy : {
+           xtype : 'HttpProxy',
+           xns : Roo.data,
+           method : 'GET',
+           url : 'https://example.com/some.data.url.json'
+        },
+        reader : {
+           xtype : 'JsonReader',
+           xns : Roo.data,
+           fields : [ 'id', 'name', whatever' ],
+           id : 'id',
+           root : 'data'
+        }
+    },
+    cm : [
+        {
+            xtype : 'ColumnModel',
+            xns : Roo.grid,
+            align : 'center',
+            cursor : 'pointer',
+            dataIndex : 'is_in_group',
+            header : "Name",
+            sortable : true,
+            renderer : function(v, x , r) {  
+            
+                return String.format("{0}", v)
+            }
+            width : 3
+        } // more columns..
+    ],
+    selModel : {
+        xtype : 'RowSelectionModel',
+        xns : Roo.bootstrap.Table
+        // you can add listeners to catch selection change here....
+    }
+     
+
+ });
+ // set any options
+ grid.render(Roo.get("some-div"));
+
+ +Currently the Table uses multiple headers to try and handle XL / Medium etc... styling + + + + * + * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet) + * @cfg {Roo.data.Store} store The data store to use + * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid. * + * @cfg {String} cls table class + * + * + * @cfg {string} empty_results Text to display for no results * @cfg {boolean} striped Should the rows be alternative striped * @cfg {boolean} bordered Add borders to the table * @cfg {boolean} hover Add hover highlighting * @cfg {boolean} condensed Format condensed - * @cfg {boolean} responsive Format condensed + * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS, + * also adds table-responsive (see bootstrap docs for details) * @cfg {Boolean} loadMask (true|false) default false * @cfg {Boolean} footerShow (true|false) generate tfoot, default true * @cfg {Boolean} headerShow (true|false) generate thead, default true * @cfg {Boolean} rowSelection (true|false) default false * @cfg {Boolean} cellSelection (true|false) default false - * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header + * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns) * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false) - + * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false) + * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop) + * + * + * @cfg {Number} minColumnWidth default 50 pixels minimum column width * * @constructor * Create a new Table * @param {Object} config The config object */ -Roo.bootstrap.Table = function(config){ +Roo.bootstrap.Table = function(config) +{ Roo.bootstrap.Table.superclass.constructor.call(this, config); - - - + // BC... this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection; this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection; this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow; this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow; + this.view = this; // compat with grid. + this.sm = this.sm || {xtype: 'RowSelectionModel'}; if (this.sm) { this.sm.grid = this; - this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table); + this.selModel = Roo.factory(this.sm, Roo.grid); this.sm = this.selModel; this.sm.xmodule = this.xmodule || false; } @@ -180,23 +236,22 @@ Roo.bootstrap.Table = function(config){ * @param {Number} columnIndex * @param {Roo.EventObject} e */ - "headercontextmenu" : true + "headercontextmenu" : true, + /** + * @event mousedown + * The raw mousedown event for the entire grid. + * @param {Roo.EventObject} e + */ + "mousedown" : true + }); }; Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { cls: false, - align: false, - bgcolor: false, - border: false, - cellpadding: false, - cellspacing: false, - frame: false, - rules: false, - sortable: false, - summary: false, - width: false, + + empty_results : '', striped : false, scrollBody : false, bordered: false, @@ -209,15 +264,20 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { loadMask : false, footerShow : true, headerShow : true, + enableColumnResize: true, rowSelection : false, cellSelection : false, layout : false, + + minColumnWidth : 50, // Roo.Element - the tbody - mainBody: false, - // Roo.Element - thead element - mainHead: false, + bodyEl: false, // Roo.Element - thead element + headEl: false, // Roo.Element - thead element + resizeProxy : false, // proxy element for dragging? + + container: false, // used by gridpanel... @@ -225,15 +285,20 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { CSS : Roo.util.CSS, + auto_hide_footer : false, + + view: false, // actually points to this.. + getAutoCreate : function() { var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this)); cfg = { tag: 'table', - cls : 'table', + cls : 'table', cn : [] }; + // this get's auto added by panel.Grid if (this.scrollBody) { cfg.cls += ' table-body-fixed'; } @@ -250,6 +315,7 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { if (this.condensed) { cfg.cls += ' table-condensed'; } + if (this.responsive) { cfg.cls += ' table-responsive'; } @@ -258,38 +324,8 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { cfg.cls+= ' ' +this.cls; } - // this lot should be simplifed... - if (this.align) { - cfg.align=this.align; - } - if (this.bgcolor) { - cfg.bgcolor=this.bgcolor; - } - if (this.border) { - cfg.border=this.border; - } - if (this.cellpadding) { - cfg.cellpadding=this.cellpadding; - } - if (this.cellspacing) { - cfg.cellspacing=this.cellspacing; - } - if (this.frame) { - cfg.frame=this.frame; - } - if (this.rules) { - cfg.rules=this.rules; - } - if (this.sortable) { - cfg.sortable=this.sortable; - } - if (this.summary) { - cfg.summary=this.summary; - } - if (this.width) { - cfg.width=this.width; - } + if (this.layout) { cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';')); } @@ -323,20 +359,17 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { //Roo.log('initEvents with ds!!!!'); - this.mainBody = this.el.select('tbody', true).first(); - this.mainHead = this.el.select('thead', true).first(); - + this.bodyEl = this.el.select('tbody', true).first(); + this.headEl = this.el.select('thead', true).first(); + this.mainFoot = this.el.select('tfoot', true).first(); - var _this = this; Roo.each(this.el.select('thead th.sortable', true).elements, function(e){ - e.on('click', _this.sort, _this); - }); + e.on('click', this.sort, this); + }, this); - this.mainBody.on("click", this.onClick, this); - this.mainBody.on("dblclick", this.onDblClick, this); // why is this done????? = it breaks dialogs?? //this.parent().el.setStyle('position', 'relative'); @@ -351,7 +384,9 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { } } - this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' }); + if(this.loadMask) { + this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' }); + } this.store.on('load', this.onLoad, this); this.store.on('beforeload', this.onBeforeLoad, this); @@ -361,14 +396,127 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { this.el.on("contextmenu", this.onContextMenu, this); - this.mainBody.on('scroll', this.onBodyScroll, this); this.cm.on("headerchange", this.onHeaderChange, this); - this.cm.on("hiddenchange", this.onHiddenChange, this, arguments); + + //?? does bodyEl get replaced on render? + this.bodyEl.on("click", this.onClick, this); + this.bodyEl.on("dblclick", this.onDblClick, this); + this.bodyEl.on('scroll', this.onBodyScroll, this); + + // guessing mainbody will work - this relays usually caught by selmodel at present. + this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]); + + + this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' }); + + + if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){ + new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..) + } + this.initCSS(); + }, + // Compatibility with grid - we implement all the view features at present. + getView : function() + { + return this; }, + initCSS : function() + { + + + var cm = this.cm, styles = []; + this.CSS.removeStyleSheet(this.id + '-cssrules'); + var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0; + // we can honour xs/sm/md/xl as widths... + // we first have to decide what widht we are currently at... + var sz = Roo.getGridSize(); + + var total = 0; + var last = -1; + var cols = []; // visable cols. + var total_abs = 0; + for(var i = 0, len = cm.getColumnCount(); i < len; i++) { + var w = cm.getColumnWidth(i, false); + if(cm.isHidden(i)){ + cols.push( { rel : false, abs : 0 }); + continue; + } + if (w !== false) { + cols.push( { rel : false, abs : w }); + total_abs += w; + last = i; // not really.. + continue; + } + var w = cm.getColumnWidth(i, sz); + if (w > 0) { + last = i + } + total += w; + cols.push( { rel : w, abs : false }); + } + + var avail = this.bodyEl.dom.clientWidth - total_abs; + + var unitWidth = Math.floor(avail / total); + var rem = avail - (unitWidth * total); + + var hidden, width, pos = 0 , splithide , left; + for(var i = 0, len = cm.getColumnCount(); i < len; i++) { + + hidden = 'display:none;'; + left = ''; + width = 'width:0px;'; + splithide = ''; + if(!cm.isHidden(i)){ + hidden = ''; + + + // we can honour xs/sm/md/xl ? + var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth); + if (w===0) { + hidden = 'display:none;'; + } + // width should return a small number... + if (i == last) { + w+=rem; // add the remaining with.. + } + pos += w; + left = "left:" + (pos -4) + "px;"; + width = "width:" + w+ "px;"; + + } + if (this.responsive) { + width = ''; + left = ''; + hidden = cm.isHidden(i) ? 'display:none;' : ''; + splithide = 'display: none;'; + } + + styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" ); + if (this.headEl) { + if (i == last) { + splithide = 'display:none;'; + } + + styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n", + '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n", + // this is the popover version.. + '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n" + ); + } + + } + //Roo.log(styles.join('')); + this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules'); + + }, + + + onContextMenu : function(e, t) { this.processEvent("contextmenu", e); @@ -502,15 +650,14 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { var rowIndex = this.getRowIndex(row); // why??? - should these not be based on SelectionModel? - if(this.cellSelection){ + //if(this.cellSelection){ this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e); - } + //} - if(this.rowSelection){ + //if(this.rowSelection){ this.fireEvent('rowclick', this, row, rowIndex, e); - } - - + //} + }, onDblClick : function(e,el) @@ -546,7 +693,19 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { this.fireEvent('rowdblclick', this, row, rowIndex, e); } }, - + findRowIndex : function(el) + { + var cell = Roo.get(el); + if(!cell) { + return false; + } + var row = cell.findParent('tr', false, true); + + if(!row || typeof(row) == 'undefined'){ + return false; + } + return this.getRowIndex(row); + }, sort : function(e,el) { var col = Roo.get(el); @@ -558,7 +717,7 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { var sort = col.attr('sort'); var dir = 'ASC'; - if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){ + if(col.select('i', true).first().hasClass('fa-arrow-up')){ dir = 'DESC'; } @@ -589,20 +748,29 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { var c = { tag: 'th', - cls : 'roo-bootstrap-thead-col-' + i, + cls : 'x-hcol-' + i, style : '', + html: cm.getColumnHeader(i) }; + var tooltip = cm.getColumnTooltip(i); + if (tooltip) { + c.tooltip = tooltip; + } + + var hh = ''; if(typeof(config.sortable) != 'undefined' && config.sortable){ - c.cls = 'sortable'; - c.html = '' + c.html; + c.cls += ' sortable'; + c.html = '' + c.html; } + // could use BS4 hidden-..-down + if(typeof(config.lgHeader) != 'undefined'){ - hh += ''; + hh += ''; } if(typeof(config.mdHeader) != 'undefined'){ @@ -629,9 +797,7 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { c.colspan = config.colspan; } - if(typeof(config.hidden) != 'undefined' && config.hidden){ - c.style += ' display:none;'; - } + // hidden is handled by CSS now if(typeof(config.dataIndex) != 'undefined'){ c.sort = config.dataIndex; @@ -643,31 +809,48 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { c.style += ' text-align:' + config.align + ';'; } - if(typeof(config.width) != 'undefined'){ + /* width is done in CSS + *if(typeof(config.width) != 'undefined'){ c.style += ' width:' + config.width + 'px;'; this.totalWidth += config.width; } else { this.totalWidth += 100; // assume minimum of 100 per column? } + */ if(typeof(config.cls) != 'undefined'){ c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls); } + // this is the bit that doesnt reall work at all... + + if (this.responsive) { + + + ['xs','sm','md','lg'].map(function(size){ + + if(typeof(config[size]) == 'undefined'){ + return; + } + + if (!config[size]) { // 0 = hidden + // BS 4 '0' is treated as hide that column and below. + c.cls += ' hidden-' + size + ' hidden' + size + '-down'; + return; + } + + c.cls += ' col-' + size + '-' + config[size] + ( + size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs + ); + + + }); + } + // at the end? + + c.html +=' '; + + - ['xs','sm','md','lg'].map(function(size){ - - if(typeof(config[size]) == 'undefined'){ - return; - } - - if (!config[size]) { // 0 = hidden - c.cls += ' hidden-' + size; - return; - } - - c.cls += ' col-' + size + '-' + config[size]; - - }); header.cn.push(c) } @@ -727,20 +910,20 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { var ds = this.store; Roo.each(this.el.select('thead th.sortable', true).elements, function(e){ - e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']); + e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']); if (_this.store.sortInfo) { if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){ - e.select('i', true).addClass(['glyphicon-arrow-up']); + e.select('i', true).addClass(['fa-arrow-up']); } if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){ - e.select('i', true).addClass(['glyphicon-arrow-down']); + e.select('i', true).addClass(['fa-arrow-down']); } } }); - var tbody = this.mainBody; + var tbody = this.bodyEl; if(ds.getCount() > 0){ ds.data.each(function(d,rowIndex){ @@ -757,6 +940,21 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { } }, this); + } else if (this.empty_results.length) { + this.el.mask(this.empty_results, 'no-spinner'); + } + + var tfoot = this.el.select('tfoot', true).first(); + + if(this.footerShow && this.auto_hide_footer && this.mainFoot){ + + this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide(); + + var total = this.ds.getTotalCount(); + + if(this.footer.pageSize < total){ + this.mainFoot.show(); + } } Roo.each(this.el.select('tbody td', true).elements, function(e){ @@ -767,11 +965,12 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { e.on('mouseout', _this.onMouseout, _this); }); this.fireEvent('rowsrendered', this); - //if(this.loadMask){ - // this.maskEl.hide(); - //} this.autoSize(); + + this.initCSS(); /// resize cols + + }, @@ -785,7 +984,7 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { if(isUpdate !== true){ this.fireEvent("beforerowremoved", this, index, record); } - var bt = this.mainBody.dom; + var bt = this.bodyEl.dom; var rows = this.el.select('tbody > tr', true).elements; @@ -809,7 +1008,7 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { { //Roo.log('on Add called'); // - note this does not handle multiple adding very well.. - var bt = this.mainBody.dom; + var bt = this.bodyEl.dom; for (var i =0 ; i < records.length;i++) { //Roo.log('call insert row Add called on ' + rowIndex + ':' + i); //Roo.log(records[i]); @@ -828,6 +1027,9 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { record = ds.getAt(index); }else{ index = ds.indexOf(record); + if (index < 0) { + return; // should not happen - but seems to + } } this.insertRow(ds, index, true); this.autoSize(); @@ -837,6 +1039,120 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { //this.layout(); this.fireEvent("rowupdated", this, index, record); }, + // private - called by RowSelection + onRowSelect : function(rowIndex){ + var row = this.getRowDom(rowIndex); + row.addClass(['bg-info','info']); + }, + // private - called by RowSelection + onRowDeselect : function(rowIndex) + { + if (rowIndex < 0) { + return; + } + var row = this.getRowDom(rowIndex); + row.removeClass(['bg-info','info']); + }, + /** + * Focuses the specified row. + * @param {Number} row The row index + */ + focusRow : function(row) + { + //Roo.log('GridView.focusRow'); + var x = this.bodyEl.dom.scrollLeft; + this.focusCell(row, 0, false); + this.bodyEl.dom.scrollLeft = x; + + }, + /** + * Focuses the specified cell. + * @param {Number} row The row index + * @param {Number} col The column index + * @param {Boolean} hscroll false to disable horizontal scrolling + */ + focusCell : function(row, col, hscroll) + { + //Roo.log('GridView.focusCell'); + var el = this.ensureVisible(row, col, hscroll); + // not sure what focusEL achives = it's a pos relative + //this.focusEl.alignTo(el, "tl-tl"); + //if(Roo.isGecko){ + // this.focusEl.focus(); + //}else{ + // this.focusEl.focus.defer(1, this.focusEl); + //} + }, + + /** + * Scrolls the specified cell into view + * @param {Number} row The row index + * @param {Number} col The column index + * @param {Boolean} hscroll false to disable horizontal scrolling + */ + ensureVisible : function(row, col, hscroll) + { + //Roo.log('GridView.ensureVisible,' + row + ',' + col); + //return null; //disable for testing. + if(typeof row != "number"){ + row = row.rowIndex; + } + if(row < 0 && row >= this.ds.getCount()){ + return null; + } + col = (col !== undefined ? col : 0); + var cm = this.cm; + while(cm.isHidden(col)){ + col++; + } + + var el = this.getCellDom(row, col); + if(!el){ + return null; + } + var c = this.bodyEl.dom; + + var ctop = parseInt(el.offsetTop, 10); + var cleft = parseInt(el.offsetLeft, 10); + var cbot = ctop + el.offsetHeight; + var cright = cleft + el.offsetWidth; + + //var ch = c.clientHeight - this.mainHd.dom.offsetHeight; + var ch = 0; //?? header is not withing the area? + var stop = parseInt(c.scrollTop, 10); + var sleft = parseInt(c.scrollLeft, 10); + var sbot = stop + ch; + var sright = sleft + c.clientWidth; + /* + Roo.log('GridView.ensureVisible:' + + ' ctop:' + ctop + + ' c.clientHeight:' + c.clientHeight + + ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight + + ' stop:' + stop + + ' cbot:' + cbot + + ' sbot:' + sbot + + ' ch:' + ch + ); + */ + if(ctop < stop){ + c.scrollTop = ctop; + //Roo.log("set scrolltop to ctop DISABLE?"); + }else if(cbot > sbot){ + //Roo.log("set scrolltop to cbot-ch"); + c.scrollTop = cbot-ch; + } + + if(hscroll !== false){ + if(cleft < sleft){ + c.scrollLeft = cleft; + }else if(cright > sright){ + c.scrollLeft = cright-c.clientWidth; + } + } + + return el; + }, + insertRow : function(dm, rowIndex, isUpdate){ @@ -846,7 +1162,7 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { //var s = this.getScrollState(); var row = this.renderRow(this.cm, this.store, rowIndex); // insert before rowIndex.. - var e = this.mainBody.createChild(row,this.getRowDom(rowIndex)); + var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex)); var _this = this; @@ -873,6 +1189,17 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex]; }, + getCellDom : function(rowIndex, colIndex) + { + var row = this.getRowDom(rowIndex); + if (row === false) { + return false; + } + var cols = row.select('td', true).elements; + return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex]; + + }, + // returns the object tree for a tr.. @@ -882,7 +1209,7 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { var row = { tag : 'tr', - cls : 'roo-bootstrap-tbody-row-' + rowIndex, + cls : 'x-row-' + rowIndex, cn : [] }; @@ -920,7 +1247,10 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { var td = { tag: 'td', - cls : rowcfg.rowClass + ' roo-bootstrap-tbody-col-' + i, + // this might end up displaying HTML? + // this is too messy... - better to only do it on columsn you know are going to be too long + //tooltip : (typeof(value) === 'object') ? '' : value, + cls : rowcfg.rowClass + ' x-col-' + i, style: '', html: (typeof(value) === 'object') ? '' : value }; @@ -933,17 +1263,19 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { td.colspan = config.colspan; } - if(typeof(config.hidden) != 'undefined' && config.hidden){ - td.style += ' display:none;'; - } + if(typeof(config.align) != 'undefined' && config.align.length){ td.style += ' text-align:' + config.align + ';'; } - + if(typeof(config.valign) != 'undefined' && config.valign.length){ + td.style += ' vertical-align:' + config.valign + ';'; + } + /* if(typeof(config.width) != 'undefined'){ td.style += ' width:' + config.width + 'px;'; } + */ if(typeof(config.cursor) != 'undefined'){ td.style += ' cursor:' + config.cursor + ';'; @@ -952,22 +1284,28 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { if(typeof(config.cls) != 'undefined'){ td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls); } - - ['xs','sm','md','lg'].map(function(size){ - - if(typeof(config[size]) == 'undefined'){ - return; - } - - if (!config[size]) { // 0 = hidden - td.cls += ' hidden-' + size; - return; - } - - td.cls += ' col-' + size + '-' + config[size]; - - }); - + if (this.responsive) { + ['xs','sm','md','lg'].map(function(size){ + + if(typeof(config[size]) == 'undefined'){ + return; + } + + + + if (!config[size]) { // 0 = hidden + // BS 4 '0' is treated as hide that column and below. + td.cls += ' hidden-' + size + ' hidden' + size + '-down'; + return; + } + + td.cls += ' col-' + size + '-' + config[size] + ( + size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs + ); + + + }); + } row.cn.push(td); } @@ -982,13 +1320,7 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { onBeforeLoad : function() { - //Roo.log('ds onBeforeLoad'); - - //this.clear(); - - //if(this.loadMask){ - // this.maskEl.show(); - //} + this.el.unmask(); // if needed. }, /** * Remove all rows @@ -1004,14 +1336,15 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { */ setRowVisibility : function(rowIndex, state) { - var bt = this.mainBody.dom; + var bt = this.bodyEl.dom; var rows = this.el.select('tbody > tr', true).elements; if(typeof(rows[rowIndex]) == 'undefined'){ return; } - rows[rowIndex].dom.style.display = state ? '' : 'none'; + rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none'); + }, @@ -1042,7 +1375,11 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { }) } }, - + /** + * get the Row Index from a dom element. + * @param {Roo.Element} row The row to look for + * @returns {Number} the row + */ getRowIndex : function(row) { var rowIndex = -1; @@ -1056,6 +1393,29 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { }); return rowIndex; + }, + /** + * get the header TH element for columnIndex + * @param {Number} columnIndex + * @returns {Roo.Element} + */ + getHeaderIndex: function(colIndex) + { + var cols = this.headEl.select('th', true).elements; + return cols[colIndex]; + }, + /** + * get the Column Index from a dom element. (using regex on x-hcol-{colid}) + * @param {domElement} cell to look for + * @returns {Number} the column + */ + getCellIndex : function(cell) + { + var id = String(cell.className).match(Roo.bootstrap.Table.cellRE); + if(id){ + return parseInt(id[1], 10); + } + return 0; }, /** * Returns the grid's underlying element = used by panel.Grid @@ -1078,17 +1438,23 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { var tfd = this.getGridEl().select('tfoot', true).first(); var cw = ctr.getWidth(); + this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw); if (tbd) { - tbd.setSize(ctr.getWidth(), - ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0)) - ); + tbd.setWidth(ctr.getWidth()); + // if the body has a max height - and then scrolls - we should perhaps set up the height here + // this needs fixing for various usage - currently only hydra job advers I think.. + //tdb.setHeight( + // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0)) + //); var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth); cw -= barsize; } cw = Math.max(cw, this.totalWidth); - this.getGridEl().select('tr',true).setWidth(cw); + this.getGridEl().select('tbody tr',true).setWidth(cw); + this.initCSS(); + // resize 'expandable coloumn? return; // we doe not have a view in this design.. @@ -1096,21 +1462,21 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { }, onBodyScroll: function() { - //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft); - if(this.mainHead){ - this.mainHead.setStyle({ + //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft); + if(this.headEl){ + this.headEl.setStyle({ 'position' : 'relative', - 'left': (-1* this.mainBody.dom.scrollLeft) + 'px' + 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px' }); } if(this.lazyLoad){ - var scrollHeight = this.mainBody.dom.scrollHeight; + var scrollHeight = this.bodyEl.dom.scrollHeight; - var scrollTop = Math.ceil(this.mainBody.getScroll().top); + var scrollTop = Math.ceil(this.bodyEl.getScroll().top); - var height = this.mainBody.getHeight(); + var height = this.bodyEl.getHeight(); if(scrollHeight - height == scrollTop) { @@ -1130,25 +1496,52 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { } }, - - onHeaderChange : function() + onColumnSplitterMoved : function(i, diff) { - Roo.log('running onHeaderChange'); + this.userResized = true; + + var cm = this.colModel; + var w = this.getHeaderIndex(i).getWidth() + diff; + + + cm.setColumnWidth(i, w, true); + this.initCSS(); + //var cid = cm.getColumnId(i); << not used in this version? + /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]); + + this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px"); + this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px"); + this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px"); +*/ + //this.updateSplitters(); + //this.layout(); << ?? + this.fireEvent("columnresize", i, w); + }, + onHeaderChange : function() + { var header = this.renderHeader(); var table = this.el.select('table', true).first(); - this.mainHead.remove(); - this.mainHead = table.createChild(header, this.mainBody, false); + this.headEl.remove(); + this.headEl = table.createChild(header, this.bodyEl, false); + + Roo.each(this.el.select('thead th.sortable', true).elements, function(e){ + e.on('click', this.sort, this); + }, this); + + if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){ + new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..) + } + }, onHiddenChange : function(colModel, colIndex, hidden) { - var thSelector = 'roo-bootstrap-thead-col-' + colIndex; - var tdSelector = 'roo-bootstrap-tbody-col-' + colIndex; - - Roo.log(thSelector); - Roo.log(tdSelector); + /* + this.cm.setHidden() + var thSelector = '#' + this.id + ' .x-hcol-' + colIndex; + var tdSelector = '#' + this.id + ' .x-col-' + colIndex; this.CSS.updateRule(thSelector, "display", ""); this.CSS.updateRule(tdSelector, "display", ""); @@ -1157,11 +1550,83 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { this.CSS.updateRule(thSelector, "display", "none"); this.CSS.updateRule(tdSelector, "display", "none"); } + */ + // onload calls initCSS() + this.onHeaderChange(); + this.onLoad(); + }, + + setColumnWidth: function(col_index, width) + { + // width = "md-2 xs-2..." + if(!this.colModel.config[col_index]) { + return; + } + + var w = width.split(" "); + + var rows = this.el.dom.getElementsByClassName("x-col-"+col_index); + + var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index); + + for(var j = 0; j < w.length; j++) { + + if(!w[j]) { + continue; + } + + var size_cls = w[j].split("-"); + + if(!Number.isInteger(size_cls[1] * 1)) { + continue; + } + + if(!this.colModel.config[col_index][size_cls[0]]) { + continue; + } + + if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) { + continue; + } + + h_row[0].classList.replace( + "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]], + "col-"+size_cls[0]+"-"+size_cls[1] + ); + + for(var i = 0; i < rows.length; i++) { + + var size_cls = w[j].split("-"); + + if(!Number.isInteger(size_cls[1] * 1)) { + continue; + } + + if(!this.colModel.config[col_index][size_cls[0]]) { + continue; + } + + if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) { + continue; + } + + rows[i].classList.replace( + "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]], + "col-"+size_cls[0]+"-"+size_cls[1] + ); + } + + this.colModel.config[col_index][size_cls[0]] = size_cls[1]; + } } - }); - +// currently only used to find the split on drag.. +Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/; - \ No newline at end of file +/** + * @depricated +*/ +Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel; +Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;