X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/grid/plugin/HeaderResizer.js diff --git a/src/grid/plugin/HeaderResizer.js b/src/grid/plugin/HeaderResizer.js new file mode 100644 index 00000000..0b7b7df2 --- /dev/null +++ b/src/grid/plugin/HeaderResizer.js @@ -0,0 +1,277 @@ +/** + * @class Ext.grid.plugin.HeaderResizer + * @extends Ext.util.Observable + * + * Plugin to add header resizing functionality to a HeaderContainer. + * Always resizing header to the left of the splitter you are resizing. + * + * Todo: Consider RTL support, columns would always calculate to the right of + * the splitter instead of to the left. + */ +Ext.define('Ext.grid.plugin.HeaderResizer', { + extend: 'Ext.util.Observable', + requires: ['Ext.dd.DragTracker', 'Ext.util.Region'], + alias: 'plugin.gridheaderresizer', + + disabled: false, + + /** + * @cfg {Boolean} dynamic + * Set to true to resize on the fly rather than using a proxy marker. Defaults to false. + */ + configs: { + dynamic: true + }, + + colHeaderCls: Ext.baseCSSPrefix + 'column-header', + + minColWidth: 40, + maxColWidth: 1000, + wResizeCursor: 'col-resize', + eResizeCursor: 'col-resize', + // not using w and e resize bc we are only ever resizing one + // column + //wResizeCursor: Ext.isWebKit ? 'w-resize' : 'col-resize', + //eResizeCursor: Ext.isWebKit ? 'e-resize' : 'col-resize', + + init: function(headerCt) { + this.headerCt = headerCt; + headerCt.on('render', this.afterHeaderRender, this, {single: true}); + }, + + /** + * @private + * AbstractComponent calls destroy on all its plugins at destroy time. + */ + destroy: function() { + if (this.tracker) { + this.tracker.destroy(); + } + }, + + afterHeaderRender: function() { + var headerCt = this.headerCt, + el = headerCt.el; + + headerCt.mon(el, 'mousemove', this.onHeaderCtMouseMove, this); + + this.tracker = Ext.create('Ext.dd.DragTracker', { + disabled: this.disabled, + onBeforeStart: Ext.Function.bind(this.onBeforeStart, this), + onStart: Ext.Function.bind(this.onStart, this), + onDrag: Ext.Function.bind(this.onDrag, this), + onEnd: Ext.Function.bind(this.onEnd, this), + tolerance: 3, + autoStart: 300, + el: el + }); + }, + + // As we mouse over individual headers, change the cursor to indicate + // that resizing is available, and cache the resize target header for use + // if/when they mousedown. + onHeaderCtMouseMove: function(e, t) { + if (this.headerCt.dragging) { + if (this.activeHd) { + this.activeHd.el.dom.style.cursor = ''; + delete this.activeHd; + } + } else { + var headerEl = e.getTarget('.' + this.colHeaderCls, 3, true), + overHeader, resizeHeader; + + if (headerEl){ + overHeader = Ext.getCmp(headerEl.id); + + // On left edge, we are resizing the previous non-hidden, base level column. + if (overHeader.isOnLeftEdge(e)) { + resizeHeader = overHeader.previousNode('gridcolumn:not([hidden]):not([isGroupHeader])'); + } + // Else, if on the right edge, we're resizing the column we are over + else if (overHeader.isOnRightEdge(e)) { + resizeHeader = overHeader; + } + // Between the edges: we are not resizing + else { + resizeHeader = null; + } + + // We *are* resizing + if (resizeHeader) { + // If we're attempting to resize a group header, that cannot be resized, + // so find its last base level column header; Group headers are sized + // by the size of their child headers. + if (resizeHeader.isGroupHeader) { + resizeHeader = resizeHeader.getVisibleGridColumns(); + resizeHeader = resizeHeader[resizeHeader.length - 1]; + } + + if (resizeHeader && !resizeHeader.fixed) { + this.activeHd = resizeHeader; + overHeader.el.dom.style.cursor = this.eResizeCursor; + } + // reset + } else { + overHeader.el.dom.style.cursor = ''; + delete this.activeHd; + } + } + } + }, + + // only start when there is an activeHd + onBeforeStart : function(e){ + var t = e.getTarget(); + // cache the activeHd because it will be cleared. + this.dragHd = this.activeHd; + + if (!!this.dragHd && !Ext.fly(t).hasCls('x-column-header-trigger') && !this.headerCt.dragging) { + //this.headerCt.dragging = true; + this.tracker.constrainTo = this.getConstrainRegion(); + return true; + } else { + this.headerCt.dragging = false; + return false; + } + }, + + // get the region to constrain to, takes into account max and min col widths + getConstrainRegion: function() { + var dragHdEl = this.dragHd.el, + region = Ext.util.Region.getRegion(dragHdEl); + + return region.adjust( + 0, + this.maxColWidth - dragHdEl.getWidth(), + 0, + this.minColWidth + ); + }, + + // initialize the left and right hand side markers around + // the header that we are resizing + onStart: function(e){ + var me = this, + dragHd = me.dragHd, + dragHdEl = dragHd.el, + width = dragHdEl.getWidth(), + headerCt = me.headerCt, + t = e.getTarget(); + + if (me.dragHd && !Ext.fly(t).hasCls('x-column-header-trigger')) { + headerCt.dragging = true; + } + + me.origWidth = width; + + // setup marker proxies + if (!me.dynamic) { + var xy = dragHdEl.getXY(), + gridSection = headerCt.up('[scrollerOwner]'), + dragHct = me.dragHd.up(':not([isGroupHeader])'), + firstSection = dragHct.up(), + lhsMarker = gridSection.getLhsMarker(), + rhsMarker = gridSection.getRhsMarker(), + el = rhsMarker.parent(), + offsetLeft = el.getLeft(true), + offsetTop = el.getTop(true), + topLeft = el.translatePoints(xy), + markerHeight = firstSection.body.getHeight() + headerCt.getHeight(), + top = topLeft.top - offsetTop; + + lhsMarker.setTop(top); + rhsMarker.setTop(top); + lhsMarker.setHeight(markerHeight); + rhsMarker.setHeight(markerHeight); + lhsMarker.setLeft(topLeft.left - offsetLeft); + rhsMarker.setLeft(topLeft.left + width - offsetLeft); + } + }, + + // synchronize the rhsMarker with the mouse movement + onDrag: function(e){ + if (!this.dynamic) { + var xy = this.tracker.getXY('point'), + gridSection = this.headerCt.up('[scrollerOwner]'), + rhsMarker = gridSection.getRhsMarker(), + el = rhsMarker.parent(), + topLeft = el.translatePoints(xy), + offsetLeft = el.getLeft(true); + + rhsMarker.setLeft(topLeft.left - offsetLeft); + // Resize as user interacts + } else { + this.doResize(); + } + }, + + onEnd: function(e){ + this.headerCt.dragging = false; + if (this.dragHd) { + if (!this.dynamic) { + var dragHd = this.dragHd, + gridSection = this.headerCt.up('[scrollerOwner]'), + lhsMarker = gridSection.getLhsMarker(), + rhsMarker = gridSection.getRhsMarker(), + currWidth = dragHd.getWidth(), + offset = this.tracker.getOffset('point'), + offscreen = -9999; + + // hide markers + lhsMarker.setLeft(offscreen); + rhsMarker.setLeft(offscreen); + } + this.doResize(); + } + }, + + doResize: function() { + if (this.dragHd) { + var dragHd = this.dragHd, + nextHd, + offset = this.tracker.getOffset('point'); + + // resize the dragHd + if (dragHd.flex) { + delete dragHd.flex; + } + + // If HeaderContainer is configured forceFit, inhibit upstream layout notification, so that + // we can also shrink the following Header by an equal amount, and *then* inform the upstream layout. + if (this.headerCt.forceFit) { + nextHd = dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])'); + if (nextHd) { + this.headerCt.componentLayout.layoutBusy = true; + } + } + + // Non-flexed Headers may never be squeezed in the event of a shortfall so + // always set the minWidth to their current width. + dragHd.minWidth = this.origWidth + offset[0]; + dragHd.setWidth(dragHd.minWidth); + + // In the case of forceFit, change the following Header width. + // Then apply the two width changes by laying out the owning HeaderContainer + if (nextHd) { + delete nextHd.flex; + nextHd.setWidth(nextHd.getWidth() - offset[0]); + this.headerCt.componentLayout.layoutBusy = false; + this.headerCt.doComponentLayout(); + } + } + }, + + disable: function() { + this.disabled = true; + if (this.tracker) { + this.tracker.disable(); + } + }, + + enable: function() { + this.disabled = false; + if (this.tracker) { + this.tracker.enable(); + } + } +}); \ No newline at end of file