From 1373b99e1e1680184270d61bca88124efd7a4c14 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Fri, 24 Apr 2015 10:36:07 -0500 Subject: [PATCH] fix(uiGrid): Use margins rather than floats for pinning Pinned containers were wrapping in certain scenarios where grids were being resi zed. It looked to the end-user like a poorly-rendered flash, and could possibly happen over and over during resizing. This fix uses margins to offset the body render container from the the left and right render containers Fixes #2997, Fixes #1957 --- misc/demo/pinning.html | 74 +++++++++++++++ src/features/pinning/less/pinning.less | 27 ++++-- src/js/core/directives/ui-pinned-container.js | 30 +++++- src/js/core/factories/GridRenderContainer.js | 93 ++++++++++++------- src/less/header.less | 2 +- src/templates/ui-grid/ui-grid.html | 13 ++- .../ui-grid/uiGridRenderContainer.html | 4 +- 7 files changed, 191 insertions(+), 52 deletions(-) create mode 100644 misc/demo/pinning.html diff --git a/misc/demo/pinning.html b/misc/demo/pinning.html new file mode 100644 index 0000000000..f1db42c7e9 --- /dev/null +++ b/misc/demo/pinning.html @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + +

Grid

+
+ + + +
+
+ + + + + diff --git a/src/features/pinning/less/pinning.less b/src/features/pinning/less/pinning.less index 9081e6eb99..d1340ce7ad 100644 --- a/src/features/pinning/less/pinning.less +++ b/src/features/pinning/less/pinning.less @@ -1,21 +1,32 @@ @import '../../../less/variables'; .ui-grid-pinned-container { - // position: absolute; - float: left; + position: absolute; + display: inline; + top: 0; + + &.ui-grid-pinned-container-left { + float: left; + left: 0; + } + + &.ui-grid-pinned-container-right { + float: right; + right: 0; + } &.ui-grid-pinned-container-left .ui-grid-header-cell:last-child { box-sizing: border-box; border-right: @gridBorderWidth solid; border-width: @gridBorderWidth; - border-color: darken(@headerVerticalBarColor, 15%); + border-right-color: darken(@headerVerticalBarColor, 15%); } &.ui-grid-pinned-container-left .ui-grid-cell:last-child { box-sizing: border-box; border-right: @gridBorderWidth solid; border-width: @gridBorderWidth; - border-color: darken(@verticalBarColor, 15%); + border-right-color: darken(@verticalBarColor, 15%); } &.ui-grid-pinned-container-left .ui-grid-header-cell:not(:last-child) .ui-grid-vertical-bar, .ui-grid-cell:not(:last-child) .ui-grid-vertical-bar { @@ -41,14 +52,14 @@ box-sizing: border-box; border-left: @gridBorderWidth solid; border-width: @gridBorderWidth; - border-color: darken(@headerVerticalBarColor, 15%); + border-left-color: darken(@headerVerticalBarColor, 15%); } &.ui-grid-pinned-container-right .ui-grid-cell:first-child { box-sizing: border-box; border-left: @gridBorderWidth solid; border-width: @gridBorderWidth; - border-color: darken(@verticalBarColor, 15%); + border-left-color: darken(@verticalBarColor, 15%); } &.ui-grid-pinned-container-right .ui-grid-header-cell:not(:first-child) .ui-grid-vertical-bar, .ui-grid-cell:not(:first-child) .ui-grid-vertical-bar { @@ -71,5 +82,5 @@ } .ui-grid-render-container-body { - float: left; -} \ No newline at end of file + // float: left; +} diff --git a/src/js/core/directives/ui-pinned-container.js b/src/js/core/directives/ui-pinned-container.js index 343ea7ab3b..f500791911 100644 --- a/src/js/core/directives/ui-pinned-container.js +++ b/src/js/core/directives/ui-pinned-container.js @@ -23,6 +23,27 @@ $elm.addClass('ui-grid-pinned-container-' + $scope.side); + // Monkey-patch the viewport width function + if ($scope.side === 'left' || $scope.side === 'right') { + grid.renderContainers[$scope.side].getViewportWidth = monkeyPatchedGetViewportWidth; + } + + function monkeyPatchedGetViewportWidth() { + /*jshint validthis: true */ + var self = this; + + var viewportWidth = 0; + self.visibleColumnCache.forEach(function (column) { + viewportWidth += column.drawnWidth; + }); + + var adjustment = self.getViewportAdjustment(); + + viewportWidth = viewportWidth + adjustment.width; + + return viewportWidth; + } + function updateContainerWidth() { if ($scope.side === 'left' || $scope.side === 'right') { var cols = grid.renderContainers[$scope.side].visibleColumnCache; @@ -35,10 +56,10 @@ return width; } } - + function updateContainerDimensions() { var ret = ''; - + // Column containers if ($scope.side === 'left' || $scope.side === 'right') { myWidth = updateContainerWidth(); @@ -49,7 +70,7 @@ $elm.attr('style', null); var myHeight = grid.renderContainers.body.getViewportHeight(); // + grid.horizontalScrollbarHeight; - + ret += '.grid' + grid.id + ' .ui-grid-pinned-container-' + $scope.side + ', .grid' + grid.id + ' .ui-grid-pinned-container-' + $scope.side + ' .ui-grid-render-container-' + $scope.side + ' .ui-grid-viewport { width: ' + myWidth + 'px; height: ' + myHeight + 'px; } '; } @@ -61,6 +82,7 @@ // Subtract our own width adjustment.width -= myWidth; + adjustment.side = $scope.side; return adjustment; }); @@ -75,4 +97,4 @@ } }; }]); -})(); \ No newline at end of file +})(); diff --git a/src/js/core/factories/GridRenderContainer.js b/src/js/core/factories/GridRenderContainer.js index 2fc1ab3447..1a33f90615 100644 --- a/src/js/core/factories/GridRenderContainer.js +++ b/src/js/core/factories/GridRenderContainer.js @@ -1,12 +1,12 @@ (function(){ angular.module('ui.grid') - + /** * @ngdoc function * @name ui.grid.class:GridRenderContainer * @description The grid has render containers, allowing the ability to have pinned columns. If the grid - * is right-to-left then there may be a right render container, if left-to-right then there may + * is right-to-left then there may be a right render container, if left-to-right then there may * be a left render container. There is always a body render container. * @param {string} name The name of the render container ('body', 'left', or 'right') * @param {Grid} grid the grid the render container is in @@ -23,7 +23,7 @@ angular.module('ui.grid') self.name = name; self.grid = grid; - + // self.rowCache = []; // self.columnCache = []; @@ -143,7 +143,7 @@ angular.module('ui.grid') * @methodOf ui.grid.class:GridRenderContainer * @description Registers an adjuster to the render container's available width or height. Adjusters are used * to tell the render container that there is something else consuming space, and to adjust it's size - * appropriately. + * appropriately. * @param {function} func the adjuster function we want to register */ @@ -170,7 +170,7 @@ angular.module('ui.grid') * @ngdoc function * @name getViewportAdjustment * @methodOf ui.grid.class:GridRenderContainer - * @description Gets the adjustment based on the viewportAdjusters. + * @description Gets the adjustment based on the viewportAdjusters. * @returns {object} a hash of { height: x, width: y }. Usually the values will be negative */ GridRenderContainer.prototype.getViewportAdjustment = function getViewportAdjustment() { @@ -185,6 +185,22 @@ angular.module('ui.grid') return adjustment; }; + GridRenderContainer.prototype.getMargin = function getMargin(side) { + var self = this; + + var amount = 0; + + self.viewportAdjusters.forEach(function (func) { + var adjustment = func.call(this, { height: 0, width: 0 }); + + if (adjustment.side && adjustment.side === side) { + amount += adjustment.width * -1; + } + }); + + return amount; + }; + GridRenderContainer.prototype.getViewportHeight = function getViewportHeight() { var self = this; @@ -194,7 +210,7 @@ angular.module('ui.grid') var adjustment = self.getViewportAdjustment(); - + viewPortHeight = viewPortHeight + adjustment.height; return viewPortHeight; @@ -203,23 +219,28 @@ angular.module('ui.grid') GridRenderContainer.prototype.getViewportWidth = function getViewportWidth() { var self = this; - var viewPortWidth = self.grid.gridWidth; + var viewportWidth = self.grid.gridWidth; //if (typeof(self.grid.verticalScrollbarWidth) !== 'undefined' && self.grid.verticalScrollbarWidth !== undefined && self.grid.verticalScrollbarWidth > 0) { // viewPortWidth = viewPortWidth - self.grid.verticalScrollbarWidth; //} + // var viewportWidth = 0;\ + // self.visibleColumnCache.forEach(function (column) { + // viewportWidth += column.drawnWidth; + // }); + var adjustment = self.getViewportAdjustment(); - - viewPortWidth = viewPortWidth + adjustment.width; - return viewPortWidth; + viewportWidth = viewportWidth + adjustment.width; + + return viewportWidth; }; GridRenderContainer.prototype.getHeaderViewportWidth = function getHeaderViewportWidth() { var self = this; - var viewPortWidth = this.getViewportWidth(); + var viewportWidth = this.getViewportWidth(); //if (typeof(self.grid.verticalScrollbarWidth) !== 'undefined' && self.grid.verticalScrollbarWidth !== undefined && self.grid.verticalScrollbarWidth > 0) { // viewPortWidth = viewPortWidth + self.grid.verticalScrollbarWidth; @@ -228,7 +249,7 @@ angular.module('ui.grid') // var adjustment = self.getViewportAdjustment(); // viewPortWidth = viewPortWidth + adjustment.width; - return viewPortWidth; + return viewportWidth; }; @@ -289,7 +310,7 @@ angular.module('ui.grid') for (var i = 0; i < newColumns.length; i++) { this.renderedColumns[i] = newColumns[i]; } - + this.updateColumnOffset(); }; @@ -396,7 +417,7 @@ angular.module('ui.grid') if ((typeof(scrollPercentage) === 'undefined' || scrollPercentage === null) && scrollTop) { scrollPercentage = scrollTop / self.getVerticalScrollLength(); } - + var rowIndex = Math.ceil(Math.min(maxRowIndex, maxRowIndex * scrollPercentage)); // Define a max row index that we can't scroll past @@ -453,7 +474,7 @@ angular.module('ui.grid') if (colIndex > maxColumnIndex) { colIndex = maxColumnIndex; } - + var newRange = []; if (columnCache.length > self.grid.options.columnVirtualizationThreshold && self.getCanvasWidth() > self.getViewportWidth()) { /* Commented the following lines because otherwise the moved column wasn't visible immediately on the new position @@ -477,7 +498,7 @@ angular.module('ui.grid') newRange = [0, Math.max(maxLen, minCols + self.grid.options.excessColumns)]; } - + self.updateViewableColumnRange(newRange); self.prevColumnScrollIndex = colIndex; @@ -509,7 +530,7 @@ angular.module('ui.grid') GridRenderContainer.prototype.headerCellWrapperStyle = function () { var self = this; - + if (self.currentFirstColumn !== 0) { var offset = self.columnOffset; @@ -529,19 +550,19 @@ angular.module('ui.grid') * @name updateColumnWidths * @propertyOf ui.grid.class:GridRenderContainer * @description Determine the appropriate column width of each column across all render containers. - * - * Column width is easy when each column has a specified width. When columns are variable width (i.e. + * + * Column width is easy when each column has a specified width. When columns are variable width (i.e. * have an * or % of the viewport) then we try to calculate so that things fit in. The problem is that * we have multiple render containers, and we don't want one render container to just take the whole viewport * when it doesn't need to - we want things to balance out across the render containers. - * + * * To do this, we use this method to calculate all the renderContainers, recognising that in a given render * cycle it'll get called once per render container, so it needs to return the same values each time. - * + * * The constraints on this method are therefore: * - must return the same value when called multiple times, to do this it needs to rely on properties of the * columns, but not properties that change when this is called (so it shouldn't rely on drawnWidth) - * + * * The general logic of this method is: * - calculate our total available width * - look at all the columns across all render containers, and work out which have widths and which have @@ -559,7 +580,7 @@ angular.module('ui.grid') var self = this; var asterisksArray = [], - asteriskNum = 0, + asteriskNum = 0, usedWidthSum = 0, ret = ''; @@ -584,22 +605,22 @@ angular.module('ui.grid') width = parseInt(column.width, 10); usedWidthSum = usedWidthSum + width; column.drawnWidth = width; - + } else if (gridUtil.endsWith(column.width, "%")) { // percentage width, set to percentage of the viewport width = parseInt(parseInt(column.width.replace(/%/g, ''), 10) / 100 * availableWidth); - + if ( width > column.maxWidth ){ width = column.maxWidth; } - + if ( width < column.minWidth ){ width = column.minWidth; } - + usedWidthSum = usedWidthSum + width; column.drawnWidth = width; - } else if (angular.isString(column.width) && column.width.indexOf('*') !== -1) { + } else if (angular.isString(column.width) && column.width.indexOf('*') !== -1) { // is an asterisk column, the gridColumn already checked the string consists only of '****' asteriskNum = asteriskNum + column.width.length; asterisksArray.push(column); @@ -621,18 +642,18 @@ angular.module('ui.grid') if ( width > column.maxWidth ){ width = column.maxWidth; } - + if ( width < column.minWidth ){ width = column.minWidth; } - + usedWidthSum = usedWidthSum + width; column.drawnWidth = width; }); } - // If the grid width didn't divide evenly into the column widths and we have pixels left over, or our - // calculated widths would have the grid narrower than the available space, + // If the grid width didn't divide evenly into the column widths and we have pixels left over, or our + // calculated widths would have the grid narrower than the available space, // dole the remainder out one by one to make everything fit var processColumnUpwards = function(column){ if ( column.drawnWidth < column.maxWidth && leftoverWidth > 0) { @@ -642,9 +663,9 @@ angular.module('ui.grid') columnsToChange = true; } }; - + var leftoverWidth = availableWidth - usedWidthSum; - var columnsToChange = true; + var columnsToChange = true; while (leftoverWidth > 0 && columnsToChange) { columnsToChange = false; @@ -661,9 +682,9 @@ angular.module('ui.grid') columnsToChange = true; } }; - + var excessWidth = usedWidthSum - availableWidth; - columnsToChange = true; + columnsToChange = true; while (excessWidth > 0 && columnsToChange) { columnsToChange = false; diff --git a/src/less/header.less b/src/less/header.less index 69b1069612..94e85589c1 100644 --- a/src/less/header.less +++ b/src/less/header.less @@ -104,7 +104,7 @@ } .ui-grid-column-menu { - position: relative; + position: absolute; } /* Slide up/down animations */ diff --git a/src/templates/ui-grid/ui-grid.html b/src/templates/ui-grid/ui-grid.html index a0c5587a0c..6a99780a25 100644 --- a/src/templates/ui-grid/ui-grid.html +++ b/src/templates/ui-grid/ui-grid.html @@ -28,9 +28,20 @@
-
+ +
+
+
+
diff --git a/src/templates/ui-grid/uiGridRenderContainer.html b/src/templates/ui-grid/uiGridRenderContainer.html index 0f62cf910f..578ffd629d 100644 --- a/src/templates/ui-grid/uiGridRenderContainer.html +++ b/src/templates/ui-grid/uiGridRenderContainer.html @@ -1,5 +1,5 @@ -
+
-
\ No newline at end of file +