/*! * ui-grid - v4.4.6 - 2018-04-06 * Copyright (c) 2018 ; License: MIT */ (function(){ 'use strict'; /** * @ngdoc overview * @name ui.grid.resizeColumns * @description * * # ui.grid.resizeColumns * * * * This module allows columns to be resized. */ var module = angular.module('ui.grid.resizeColumns', ['ui.grid']); module.service('uiGridResizeColumnsService', ['gridUtil', '$q', '$rootScope', function (gridUtil, $q, $rootScope) { var service = { defaultGridOptions: function(gridOptions){ //default option to true unless it was explicitly set to false /** * @ngdoc object * @name ui.grid.resizeColumns.api:GridOptions * * @description GridOptions for resizeColumns feature, these are available to be * set using the ui-grid {@link ui.grid.class:GridOptions gridOptions} */ /** * @ngdoc object * @name enableColumnResizing * @propertyOf ui.grid.resizeColumns.api:GridOptions * @description Enable column resizing on the entire grid *
Defaults to true */ gridOptions.enableColumnResizing = gridOptions.enableColumnResizing !== false; //legacy support //use old name if it is explicitly false if (gridOptions.enableColumnResize === false){ gridOptions.enableColumnResizing = false; } }, colResizerColumnBuilder: function (colDef, col, gridOptions) { var promises = []; /** * @ngdoc object * @name ui.grid.resizeColumns.api:ColumnDef * * @description ColumnDef for resizeColumns feature, these are available to be * set using the ui-grid {@link ui.grid.class:GridOptions.columnDef gridOptions.columnDefs} */ /** * @ngdoc object * @name enableColumnResizing * @propertyOf ui.grid.resizeColumns.api:ColumnDef * @description Enable column resizing on an individual column *
Defaults to GridOptions.enableColumnResizing */ //default to true unless gridOptions or colDef is explicitly false colDef.enableColumnResizing = colDef.enableColumnResizing === undefined ? gridOptions.enableColumnResizing : colDef.enableColumnResizing; //legacy support of old option name if (colDef.enableColumnResize === false){ colDef.enableColumnResizing = false; } return $q.all(promises); }, registerPublicApi: function (grid) { /** * @ngdoc object * @name ui.grid.resizeColumns.api:PublicApi * @description Public Api for column resize feature. */ var publicApi = { events: { /** * @ngdoc event * @name columnSizeChanged * @eventOf ui.grid.resizeColumns.api:PublicApi * @description raised when column is resized *
                 *      gridApi.colResizable.on.columnSizeChanged(scope,function(colDef, deltaChange){})
                 * 
* @param {object} colDef the column that was resized * @param {integer} delta of the column size change */ colResizable: { columnSizeChanged: function (colDef, deltaChange) { } } } }; grid.api.registerEventsFromObject(publicApi.events); }, fireColumnSizeChanged: function (grid, colDef, deltaChange) { $rootScope.$applyAsync(function () { if ( grid.api.colResizable ){ grid.api.colResizable.raise.columnSizeChanged(colDef, deltaChange); } else { gridUtil.logError("The resizeable api is not registered, this may indicate that you've included the module but not added the 'ui-grid-resize-columns' directive to your grid definition. Cannot raise any events."); } }); }, // get either this column, or the column next to this column, to resize, // returns the column we're going to resize findTargetCol: function(col, position, rtlMultiplier){ var renderContainer = col.getRenderContainer(); if (position === 'left') { // Get the column to the left of this one var colIndex = renderContainer.visibleColumnCache.indexOf(col); return renderContainer.visibleColumnCache[colIndex - 1 * rtlMultiplier]; } else { return col; } } }; return service; }]); /** * @ngdoc directive * @name ui.grid.resizeColumns.directive:uiGridResizeColumns * @element div * @restrict A * @description * Enables resizing for all columns on the grid. If, for some reason, you want to use the ui-grid-resize-columns directive, but not allow column resizing, you can explicitly set the * option to false. This prevents resizing for the entire grid, regardless of individual columnDef options. * * @example
*/ module.directive('uiGridResizeColumns', ['gridUtil', 'uiGridResizeColumnsService', function (gridUtil, uiGridResizeColumnsService) { return { replace: true, priority: 0, require: '^uiGrid', scope: false, compile: function () { return { pre: function ($scope, $elm, $attrs, uiGridCtrl) { uiGridResizeColumnsService.defaultGridOptions(uiGridCtrl.grid.options); uiGridCtrl.grid.registerColumnBuilder( uiGridResizeColumnsService.colResizerColumnBuilder); uiGridResizeColumnsService.registerPublicApi(uiGridCtrl.grid); }, post: function ($scope, $elm, $attrs, uiGridCtrl) { } }; } }; }]); // Extend the uiGridHeaderCell directive module.directive('uiGridHeaderCell', ['gridUtil', '$templateCache', '$compile', '$q', 'uiGridResizeColumnsService', 'uiGridConstants', function (gridUtil, $templateCache, $compile, $q, uiGridResizeColumnsService, uiGridConstants) { return { // Run after the original uiGridHeaderCell priority: -10, require: '^uiGrid', // scope: false, compile: function() { return { post: function ($scope, $elm, $attrs, uiGridCtrl) { var grid = uiGridCtrl.grid; if (grid.options.enableColumnResizing) { var columnResizerElm = $templateCache.get('ui-grid/columnResizer'); var rtlMultiplier = 1; //when in RTL mode reverse the direction using the rtlMultiplier and change the position to left if (grid.isRTL()) { $scope.position = 'left'; rtlMultiplier = -1; } var displayResizers = function(){ // remove any existing resizers. var resizers = $elm[0].getElementsByClassName('ui-grid-column-resizer'); for ( var i = 0; i < resizers.length; i++ ){ angular.element(resizers[i]).remove(); } // get the target column for the left resizer var otherCol = uiGridResizeColumnsService.findTargetCol($scope.col, 'left', rtlMultiplier); var renderContainer = $scope.col.getRenderContainer(); // Don't append the left resizer if this is the first column or the column to the left of this one has resizing disabled if (otherCol && renderContainer.visibleColumnCache.indexOf($scope.col) !== 0 && otherCol.colDef.enableColumnResizing !== false) { var resizerLeft = angular.element(columnResizerElm).clone(); resizerLeft.attr('position', 'left'); $elm.prepend(resizerLeft); $compile(resizerLeft)($scope); } // Don't append the right resizer if this column has resizing disabled if ($scope.col.colDef.enableColumnResizing !== false) { var resizerRight = angular.element(columnResizerElm).clone(); resizerRight.attr('position', 'right'); $elm.append(resizerRight); $compile(resizerRight)($scope); } }; displayResizers(); var waitDisplay = function() { $scope.$applyAsync(displayResizers); }; var dataChangeDereg = grid.registerDataChangeCallback( waitDisplay, [uiGridConstants.dataChange.COLUMN] ); $scope.$on( '$destroy', dataChangeDereg ); } } }; } }; }]); /** * @ngdoc directive * @name ui.grid.resizeColumns.directive:uiGridColumnResizer * @element div * @restrict A * * @description * Draggable handle that controls column resizing. * * @example
// TODO: e2e specs? // TODO: post-resize a horizontal scroll event should be fired
*/ module.directive('uiGridColumnResizer', ['$document', 'gridUtil', 'uiGridConstants', 'uiGridResizeColumnsService', function ($document, gridUtil, uiGridConstants, uiGridResizeColumnsService) { var resizeOverlay = angular.element('
'); var resizer = { priority: 0, scope: { col: '=', position: '@', renderIndex: '=' }, require: '?^uiGrid', link: function ($scope, $elm, $attrs, uiGridCtrl) { var startX = 0, x = 0, gridLeft = 0, rtlMultiplier = 1; //when in RTL mode reverse the direction using the rtlMultiplier and change the position to left if (uiGridCtrl.grid.isRTL()) { $scope.position = 'left'; rtlMultiplier = -1; } if ($scope.position === 'left') { $elm.addClass('left'); } else if ($scope.position === 'right') { $elm.addClass('right'); } // Refresh the grid canvas // takes an argument representing the diff along the X-axis that the resize had function refreshCanvas(xDiff) { // Then refresh the grid canvas, rebuilding the styles so that the scrollbar updates its size uiGridCtrl.grid.refreshCanvas(true).then( function() { uiGridCtrl.grid.queueGridRefresh(); }); } // Check that the requested width isn't wider than the maxWidth, or narrower than the minWidth // Returns the new recommended with, after constraints applied function constrainWidth(col, width){ var newWidth = width; // If the new width would be less than the column's allowably minimum width, don't allow it if (col.minWidth && newWidth < col.minWidth) { newWidth = col.minWidth; } else if (col.maxWidth && newWidth > col.maxWidth) { newWidth = col.maxWidth; } return newWidth; } /* * Our approach to event handling aims to deal with both touch devices and mouse devices * We register down handlers on both touch and mouse. When a touchstart or mousedown event * occurs, we register the corresponding touchmove/touchend, or mousemove/mouseend events. * * This way we can listen for both without worrying about the fact many touch devices also emulate * mouse events - basically whichever one we hear first is what we'll go with. */ function moveFunction(event, args) { if (event.originalEvent) { event = event.originalEvent; } event.preventDefault(); x = (event.targetTouches ? event.targetTouches[0] : event).clientX - gridLeft; if (x < 0) { x = 0; } else if (x > uiGridCtrl.grid.gridWidth) { x = uiGridCtrl.grid.gridWidth; } var col = uiGridResizeColumnsService.findTargetCol($scope.col, $scope.position, rtlMultiplier); // Don't resize if it's disabled on this column if (col.colDef.enableColumnResizing === false) { return; } if (!uiGridCtrl.grid.element.hasClass('column-resizing')) { uiGridCtrl.grid.element.addClass('column-resizing'); } // Get the diff along the X axis var xDiff = x - startX; // Get the width that this mouse would give the column var newWidth = parseInt(col.drawnWidth + xDiff * rtlMultiplier, 10); // check we're not outside the allowable bounds for this column x = x + ( constrainWidth(col, newWidth) - newWidth ) * rtlMultiplier; resizeOverlay.css({ left: x + 'px' }); uiGridCtrl.fireEvent(uiGridConstants.events.ITEM_DRAGGING); } function upFunction(event, args) { if (event.originalEvent) { event = event.originalEvent; } event.preventDefault(); uiGridCtrl.grid.element.removeClass('column-resizing'); resizeOverlay.remove(); // Resize the column x = (event.changedTouches ? event.changedTouches[0] : event).clientX - gridLeft; var xDiff = x - startX; if (xDiff === 0) { // no movement, so just reset event handlers, including turning back on both // down events - we turned one off when this event started offAllEvents(); onDownEvents(); return; } var col = uiGridResizeColumnsService.findTargetCol($scope.col, $scope.position, rtlMultiplier); // Don't resize if it's disabled on this column if (col.colDef.enableColumnResizing === false) { return; } // Get the new width var newWidth = parseInt(col.drawnWidth + xDiff * rtlMultiplier, 10); // check we're not outside the allowable bounds for this column col.width = constrainWidth(col, newWidth); col.hasCustomWidth = true; refreshCanvas(xDiff); uiGridResizeColumnsService.fireColumnSizeChanged(uiGridCtrl.grid, col.colDef, xDiff); // stop listening of up and move events - wait for next down // reset the down events - we will have turned one off when this event started offAllEvents(); onDownEvents(); } var downFunction = function(event, args) { if (event.originalEvent) { event = event.originalEvent; } event.stopPropagation(); // Get the left offset of the grid // gridLeft = uiGridCtrl.grid.element[0].offsetLeft; gridLeft = uiGridCtrl.grid.element[0].getBoundingClientRect().left; // Get the starting X position, which is the X coordinate of the click minus the grid's offset startX = (event.targetTouches ? event.targetTouches[0] : event).clientX - gridLeft; // Append the resizer overlay uiGridCtrl.grid.element.append(resizeOverlay); // Place the resizer overlay at the start position resizeOverlay.css({ left: startX }); // Add handlers for move and up events - if we were mousedown then we listen for mousemove and mouseup, if // we were touchdown then we listen for touchmove and touchup. Also remove the handler for the equivalent // down event - so if we're touchdown, then remove the mousedown handler until this event is over, if we're // mousedown then remove the touchdown handler until this event is over, this avoids processing duplicate events if ( event.type === 'touchstart' ){ $document.on('touchend', upFunction); $document.on('touchmove', moveFunction); $elm.off('mousedown', downFunction); } else { $document.on('mouseup', upFunction); $document.on('mousemove', moveFunction); $elm.off('touchstart', downFunction); } }; var onDownEvents = function() { $elm.on('mousedown', downFunction); $elm.on('touchstart', downFunction); }; var offAllEvents = function() { $document.off('mouseup', upFunction); $document.off('touchend', upFunction); $document.off('mousemove', moveFunction); $document.off('touchmove', moveFunction); $elm.off('mousedown', downFunction); $elm.off('touchstart', downFunction); }; onDownEvents(); // On doubleclick, resize to fit all rendered cells var dblClickFn = function(event, args){ event.stopPropagation(); var col = uiGridResizeColumnsService.findTargetCol($scope.col, $scope.position, rtlMultiplier); // Don't resize if it's disabled on this column if (col.colDef.enableColumnResizing === false) { return; } // Go through the rendered rows and find out the max size for the data in this column var maxWidth = 0; var xDiff = 0; // Get the parent render container element var renderContainerElm = gridUtil.closestElm($elm, '.ui-grid-render-container'); // Get the cell contents so we measure correctly. For the header cell we have to account for the sort icon and the menu buttons, if present var cells = renderContainerElm.querySelectorAll('.' + uiGridConstants.COL_CLASS_PREFIX + col.uid + ' .ui-grid-cell-contents'); Array.prototype.forEach.call(cells, function (cell) { // Get the cell width // gridUtil.logDebug('width', gridUtil.elementWidth(cell)); // Account for the menu button if it exists var menuButton; if (angular.element(cell).parent().hasClass('ui-grid-header-cell')) { menuButton = angular.element(cell).parent()[0].querySelectorAll('.ui-grid-column-menu-button'); } gridUtil.fakeElement(cell, {}, function(newElm) { // Make the element float since it's a div and can expand to fill its container var e = angular.element(newElm); e.attr('style', 'float: left'); var width = gridUtil.elementWidth(e); if (menuButton) { var menuButtonWidth = gridUtil.elementWidth(menuButton); width = width + menuButtonWidth; } if (width > maxWidth) { maxWidth = width; } }); }); // check we're not outside the allowable bounds for this column var newWidth = constrainWidth(col, maxWidth); xDiff = newWidth - col.drawnWidth; col.width = newWidth; col.hasCustomWidth = true; refreshCanvas(xDiff); uiGridResizeColumnsService.fireColumnSizeChanged(uiGridCtrl.grid, col.colDef, xDiff); }; $elm.on('dblclick', dblClickFn); $elm.on('$destroy', function() { $elm.off('dblclick', dblClickFn); offAllEvents(); }); } }; return resizer; }]); })();