3 * Copyright(c) 2006-2010 Sencha Inc.
5 * http://www.sencha.com/license
8 * @class Ext.ux.tree.TreeGrid
9 * @extends Ext.tree.TreePanel
13 Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, {
17 borderWidth : Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
22 reserveScrollOffset : true,
25 columnsText : 'Columns',
27 initComponent : function() {
29 this.root = new Ext.tree.AsyncTreeNode({text: 'Root'});
32 // initialize the loader
35 l = new Ext.ux.tree.TreeGridLoader({
36 dataUrl: this.dataUrl,
37 requestMethod: this.requestMethod,
40 }else if(Ext.isObject(l) && !l.load){
41 l = new Ext.ux.tree.TreeGridLoader(l);
45 Ext.ux.tree.TreeGrid.superclass.initComponent.call(this);
50 this.treeGridSorter = new Ext.ux.tree.TreeGridSorter(this, this.enableSort);
53 if(this.columnResize){
54 this.colResizer = new Ext.tree.ColumnResizer(this.columnResize);
55 this.colResizer.init(this);
59 if(!this.internalTpl){
60 this.internalTpl = new Ext.XTemplate(
61 '<div class="x-grid3-header">',
62 '<div class="x-treegrid-header-inner">',
63 '<div class="x-grid3-header-offset">',
64 '<table style="table-layout: fixed;" cellspacing="0" cellpadding="0" border="0"><colgroup><tpl for="columns"><col /></tpl></colgroup>',
65 '<thead><tr class="x-grid3-hd-row">',
66 '<tpl for="columns">',
67 '<td class="x-grid3-hd x-grid3-cell x-treegrid-hd" style="text-align: {align};" id="', this.id, '-xlhd-{#}">',
68 '<div class="x-grid3-hd-inner x-treegrid-hd-inner" unselectable="on">',
69 this.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
70 '{header}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
77 '<div class="x-treegrid-root-node">',
78 '<table class="x-treegrid-root-table" cellpadding="0" cellspacing="0" style="table-layout: fixed;"></table>',
83 if(!this.colgroupTpl) {
84 this.colgroupTpl = new Ext.XTemplate(
85 '<colgroup><tpl for="columns"><col style="width: {width}px"/></tpl></colgroup>'
90 initColumns : function() {
91 var cs = this.columns,
96 for(i = 0; i < len; i++){
99 c.xtype = c.xtype ? (/^tg/.test(c.xtype) ? c.xtype : 'tg' + c.xtype) : 'tgcolumn';
105 if(this.enableSort !== false && c.sortable !== false) {
107 this.enableSort = true;
111 this.columns = columns;
114 onRender : function(){
115 Ext.tree.TreePanel.superclass.onRender.apply(this, arguments);
117 this.el.addClass('x-treegrid');
119 this.outerCt = this.body.createChild({
120 cls:'x-tree-root-ct x-treegrid-ct ' + (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines')
123 this.internalTpl.overwrite(this.outerCt, {columns: this.columns});
125 this.mainHd = Ext.get(this.outerCt.dom.firstChild);
126 this.innerHd = Ext.get(this.mainHd.dom.firstChild);
127 this.innerBody = Ext.get(this.outerCt.dom.lastChild);
128 this.innerCt = Ext.get(this.innerBody.dom.firstChild);
130 this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns});
132 if(this.hideHeaders){
133 this.el.child('.x-grid3-header').setDisplayed('none');
135 else if(this.enableHdMenu !== false){
136 this.hmenu = new Ext.menu.Menu({id: this.id + '-hctx'});
137 if(this.enableColumnHide !== false){
138 this.colMenu = new Ext.menu.Menu({id: this.id + '-hcols-menu'});
141 beforeshow: this.beforeColMenuShow,
142 itemclick: this.handleHdMenuClick
147 text: this.columnsText,
149 iconCls: 'x-cols-icon'
152 this.hmenu.on('itemclick', this.handleHdMenuClick, this);
156 setRootNode : function(node){
157 node.attributes.uiProvider = Ext.ux.tree.TreeGridRootNodeUI;
158 node = Ext.ux.tree.TreeGrid.superclass.setRootNode.call(this, node);
160 this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns});
165 clearInnerCt : function(){
167 var dom = this.innerCt.dom;
168 while(dom.firstChild){
169 dom.removeChild(dom.firstChild);
172 Ext.ux.tree.TreeGrid.superclass.clearInnerCt.call(this);
176 initEvents : function() {
177 Ext.ux.tree.TreeGrid.superclass.initEvents.apply(this, arguments);
179 this.mon(this.innerBody, 'scroll', this.syncScroll, this);
180 this.mon(this.innerHd, 'click', this.handleHdDown, this);
181 this.mon(this.mainHd, {
183 mouseover: this.handleHdOver,
184 mouseout: this.handleHdOut
188 onResize : function(w, h) {
189 Ext.ux.tree.TreeGrid.superclass.onResize.apply(this, arguments);
191 var bd = this.innerBody.dom;
192 var hd = this.innerHd.dom;
199 bd.style.height = this.body.getHeight(true) - hd.offsetHeight + 'px';
203 var sw = Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
204 if(this.reserveScrollOffset || ((bd.offsetWidth - bd.clientWidth) > 10)){
205 this.setScrollOffset(sw);
208 setTimeout(function(){
209 me.setScrollOffset(bd.offsetWidth - bd.clientWidth > 10 ? sw : 0);
215 updateColumnWidths : function() {
216 var cols = this.columns,
217 colCount = cols.length,
218 groups = this.outerCt.query('colgroup'),
219 groupCount = groups.length,
222 for(i = 0; i<colCount; i++) {
224 for(j = 0; j<groupCount; j++) {
226 g.childNodes[i].style.width = (c.hidden ? 0 : c.width) + 'px';
230 for(i = 0, groups = this.innerHd.query('td'), len = groups.length; i<len; i++) {
231 c = Ext.fly(groups[i]);
232 if(cols[i] && cols[i].hidden) {
233 c.addClass('x-treegrid-hd-hidden');
236 c.removeClass('x-treegrid-hd-hidden');
240 var tcw = this.getTotalColumnWidth();
241 Ext.fly(this.innerHd.dom.firstChild).setWidth(tcw + (this.scrollOffset || 0));
242 this.outerCt.select('table').setWidth(tcw);
243 this.syncHeaderScroll();
246 getVisibleColumns : function() {
252 for(i = 0; i<len; i++) {
260 getTotalColumnWidth : function() {
262 for(var i = 0, cs = this.getVisibleColumns(), len = cs.length; i<len; i++) {
263 total += cs[i].width;
268 setScrollOffset : function(scrollOffset) {
269 this.scrollOffset = scrollOffset;
270 this.updateColumnWidths();
274 handleHdDown : function(e, t){
275 var hd = e.getTarget('.x-treegrid-hd');
277 if(hd && Ext.fly(t).hasClass('x-grid3-hd-btn')){
278 var ms = this.hmenu.items,
280 index = this.findHeaderIndex(hd),
285 Ext.fly(hd).addClass('x-grid3-hd-menu-open');
286 this.hdCtxIndex = index;
288 this.fireEvent('headerbuttonclick', ms, c, hd, index);
290 this.hmenu.on('hide', function(){
291 Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
292 }, this, {single:true});
294 this.hmenu.show(t, 'tl-bl?');
297 var index = this.findHeaderIndex(hd);
298 this.fireEvent('headerclick', this.columns[index], hd, index);
303 handleHdOver : function(e, t){
304 var hd = e.getTarget('.x-treegrid-hd');
305 if(hd && !this.headersDisabled){
306 index = this.findHeaderIndex(hd);
307 this.activeHdRef = t;
308 this.activeHdIndex = index;
309 var el = Ext.get(hd);
310 this.activeHdRegion = el.getRegion();
311 el.addClass('x-grid3-hd-over');
312 this.activeHdBtn = el.child('.x-grid3-hd-btn');
313 if(this.activeHdBtn){
314 this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
320 handleHdOut : function(e, t){
321 var hd = e.getTarget('.x-treegrid-hd');
322 if(hd && (!Ext.isIE || !e.within(hd, true))){
323 this.activeHdRef = null;
324 Ext.fly(hd).removeClass('x-grid3-hd-over');
325 hd.style.cursor = '';
329 findHeaderIndex : function(hd){
331 var cs = hd.parentNode.childNodes;
332 for(var i = 0, c; c = cs[i]; i++){
341 beforeColMenuShow : function(){
342 var cols = this.columns,
343 colCount = cols.length,
345 this.colMenu.removeAll();
346 for(i = 1; i < colCount; i++){
348 if(c.hideable !== false){
349 this.colMenu.add(new Ext.menu.CheckItem({
354 disabled: c.hideable === false
361 handleHdMenuClick : function(item){
362 var index = this.hdCtxIndex,
363 id = item.getItemId();
365 if(this.fireEvent('headermenuclick', this.columns[index], id, index) !== false) {
366 index = id.substr(4);
367 if(index > 0 && this.columns[index]) {
368 this.setColumnVisible(index, !item.checked);
375 setColumnVisible : function(index, visible) {
376 this.columns[index].hidden = !visible;
377 this.updateColumnWidths();
381 * Scrolls the grid to the top
383 scrollToTop : function(){
384 this.innerBody.dom.scrollTop = 0;
385 this.innerBody.dom.scrollLeft = 0;
389 syncScroll : function(){
390 this.syncHeaderScroll();
391 var mb = this.innerBody.dom;
392 this.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
396 syncHeaderScroll : function(){
397 var mb = this.innerBody.dom;
398 this.innerHd.dom.scrollLeft = mb.scrollLeft;
399 this.innerHd.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
402 registerNode : function(n) {
403 Ext.ux.tree.TreeGrid.superclass.registerNode.call(this, n);
404 if(!n.uiProvider && !n.isRoot && !n.ui.isTreeGridNodeUI) {
405 n.ui = new Ext.ux.tree.TreeGridNodeUI(n);
410 Ext.reg('treegrid', Ext.ux.tree.TreeGrid);