UNPKG

ag-grid

Version:

Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components

908 lines 75.3 kB
/** * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components * @version v18.1.2 * @link http://www.ag-grid.com/ * @license MIT */ "use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); var utils_1 = require("../utils"); var resizeObserver_1 = require("../resizeObserver"); var gridOptionsWrapper_1 = require("../gridOptionsWrapper"); var columnController_1 = require("../columnController/columnController"); var columnApi_1 = require("../columnController/columnApi"); var rowRenderer_1 = require("../rendering/rowRenderer"); var context_1 = require("../context/context"); var eventService_1 = require("../eventService"); var events_1 = require("../events"); var dragService_1 = require("../dragAndDrop/dragService"); var constants_1 = require("../constants"); var selectionController_1 = require("../selectionController"); var csvCreator_1 = require("../csvCreator"); var mouseEventService_1 = require("./mouseEventService"); var focusedCellController_1 = require("../focusedCellController"); var scrollVisibleService_1 = require("./scrollVisibleService"); var rowContainerComponent_1 = require("../rendering/rowContainerComponent"); var paginationProxy_1 = require("../rowModels/paginationProxy"); var popupEditorWrapper_1 = require("../rendering/cellEditors/popupEditorWrapper"); var alignedGridsService_1 = require("../alignedGridsService"); var pinnedRowModel_1 = require("../rowModels/pinnedRowModel"); var gridApi_1 = require("../gridApi"); var animationFrameService_1 = require("../misc/animationFrameService"); var rowComp_1 = require("../rendering/rowComp"); var navigationService_1 = require("./navigationService"); var valueService_1 = require("../valueService/valueService"); var touchListener_1 = require("../widgets/touchListener"); var componentRecipes_1 = require("../components/framework/componentRecipes"); var dragAndDropService_1 = require("../dragAndDrop/dragAndDropService"); var rowDragFeature_1 = require("./rowDragFeature"); var heightScaler_1 = require("../rendering/heightScaler"); var component_1 = require("../widgets/component"); var autoHeightCalculator_1 = require("../rendering/autoHeightCalculator"); var columnAnimationService_1 = require("../rendering/columnAnimationService"); var autoWidthCalculator_1 = require("../rendering/autoWidthCalculator"); var beans_1 = require("../rendering/beans"); var componentAnnotations_1 = require("../widgets/componentAnnotations"); var headerRootComp_1 = require("../headerRendering/headerRootComp"); // in the html below, it is important that there are no white space between some of the divs, as if there is white space, // it won't render correctly in safari, as safari renders white space as a gap var GRID_PANEL_NORMAL_TEMPLATE = "<div class=\"ag-root ag-font-style\" role=\"grid\">\n <ag-header-root ref=\"headerRoot\"></ag-header-root>\n <div class=\"ag-floating-top\" ref=\"eTop\" role=\"presentation\">\n <div class=\"ag-pinned-left-floating-top\" ref=\"eLeftTop\" role=\"presentation\"></div>\n <div class=\"ag-floating-top-viewport\" ref=\"eTopViewport\" role=\"presentation\">\n <div class=\"ag-floating-top-container\" ref=\"eTopContainer\" role=\"presentation\"></div>\n </div>\n <div class=\"ag-pinned-right-floating-top\" ref=\"eRightTop\" role=\"presentation\"></div>\n <div class=\"ag-floating-top-full-width-container\" ref=\"eTopFullWidthContainer\" role=\"presentation\"></div>\n </div>\n <div class=\"ag-body\" ref=\"eBody\" role=\"presentation\">\n <div class=\"ag-pinned-left-cols-viewport-wrapper\" ref=\"eLeftViewportWrapper\" role=\"presentation\">\n <div class=\"ag-pinned-left-cols-viewport\" ref=\"eLeftViewport\" role=\"presentation\">\n <div class=\"ag-pinned-left-cols-container\" ref=\"eLeftContainer\" role=\"presentation\"></div>\n </div>\n </div>\n <div class=\"ag-body-viewport-wrapper\" role=\"presentation\">\n <div class=\"ag-body-viewport\" ref=\"eBodyViewport\" role=\"presentation\">\n <div class=\"ag-body-container\" ref=\"eBodyContainer\" role=\"presentation\"></div>\n </div>\n </div>\n <div class=\"ag-pinned-right-cols-viewport-wrapper\" ref=\"eRightViewportWrapper\" role=\"presentation\">\n <div class=\"ag-pinned-right-cols-viewport\" ref=\"eRightViewport\" role=\"presentation\">\n <div class=\"ag-pinned-right-cols-container\" ref=\"eRightContainer\" role=\"presentation\"></div>\n </div>\n </div>\n <div class=\"ag-full-width-viewport-wrapper\" ref=\"eFullWidthViewportWrapper\" role=\"presentation\">\n <div class=\"ag-full-width-viewport\" ref=\"eFullWidthViewport\" role=\"presentation\">\n <div class=\"ag-full-width-container\" ref=\"eFullWidthContainer\" role=\"presentation\"></div>\n </div>\n </div>\n </div>\n <div class=\"ag-floating-bottom\" ref=\"eBottom\" role=\"presentation\">\n <div class=\"ag-pinned-left-floating-bottom\" ref=\"eLeftBottom\" role=\"presentation\"></div>\n <div class=\"ag-floating-bottom-viewport\" ref=\"eBottomViewport\" role=\"presentation\">\n <div class=\"ag-floating-bottom-container\" ref=\"eBottomContainer\" role=\"presentation\"></div>\n </div>\n <div class=\"ag-pinned-right-floating-bottom\" ref=\"eRightBottom\" role=\"presentation\"></div>\n <div class=\"ag-floating-bottom-full-width-container\" ref=\"eBottomFullWidthContainer\" role=\"presentation\"></div>\n </div>\n <div class=\"ag-overlay\" ref=\"eOverlay\"></div>\n </div>"; var GridPanel = (function (_super) { __extends(GridPanel, _super); function GridPanel() { var _this = _super.call(this, GRID_PANEL_NORMAL_TEMPLATE) || this; _this.scrollLeft = -1; _this.nextScrollLeft = -1; _this.scrollTop = -1; _this.nextScrollTop = -1; _this.verticalRedrawNeeded = false; return _this; } GridPanel.prototype.getVScrollPosition = function () { var result = { top: this.eBodyViewport.scrollTop, bottom: this.eBodyViewport.scrollTop + this.eBodyViewport.offsetHeight }; return result; }; // used by range controller GridPanel.prototype.getHScrollPosition = function () { var result = { left: this.eBodyViewport.scrollLeft, right: this.eBodyViewport.scrollTop + this.eBodyViewport.offsetWidth }; return result; }; // we override this, as the base class is missing the annotation GridPanel.prototype.destroy = function () { _super.prototype.destroy.call(this); }; GridPanel.prototype.onRowDataChanged = function () { this.showOrHideOverlay(); }; GridPanel.prototype.showOrHideOverlay = function () { if (this.paginationProxy.isEmpty() && !this.gridOptionsWrapper.isSuppressNoRowsOverlay()) { this.showNoRowsOverlay(); } else { this.hideOverlay(); } }; GridPanel.prototype.onNewColumnsLoaded = function () { // hide overlay if columns and rows exist, this can happen if columns are loaded after data. // this problem exists before of the race condition between the services (column controller in this case) // and the view (grid panel). if the model beans were all initialised first, and then the view beans second, // this race condition would not happen. if (this.columnController.isReady() && !this.paginationProxy.isEmpty()) { this.hideOverlay(); } }; GridPanel.prototype.init = function () { this.instantiate(this.context); // makes code below more readable if we pull 'forPrint' out this.scrollWidth = this.gridOptionsWrapper.getScrollbarWidth(); this.enableRtl = this.gridOptionsWrapper.isEnableRtl(); this.useAnimationFrame = !this.gridOptionsWrapper.isSuppressAnimationFrame(); // if the browser is Windows based, then the scrollbars take up space, and we clip by // the width of the scrollbar. however if the scroll bars do not take up space (iOS) // then they overlay on top of the div, so we clip some extra blank space instead. this.scrollClipWidth = this.scrollWidth > 0 ? this.scrollWidth : 20; // all of these element have different CSS when layout changes this.gridOptionsWrapper.addLayoutElement(this.getGui()); this.gridOptionsWrapper.addLayoutElement(this.eBody); this.gridOptionsWrapper.addLayoutElement(this.eBodyViewport); this.gridOptionsWrapper.addLayoutElement(this.eTopViewport); this.gridOptionsWrapper.addLayoutElement(this.eBodyContainer); this.suppressScrollOnFloatingRow(); this.setupRowAnimationCssClass(); this.buildRowContainerComponents(); this.addEventListeners(); this.addDragListeners(); this.addScrollListener(); if (this.gridOptionsWrapper.isSuppressHorizontalScroll()) { this.eBodyViewport.style.overflowX = 'hidden'; } this.setupOverlay(); if (this.gridOptionsWrapper.isRowModelDefault() && !this.gridOptionsWrapper.getRowData()) { this.showLoadingOverlay(); } this.setPinnedContainersVisible(); this.setBodyAndHeaderHeights(); this.disableBrowserDragging(); this.addShortcutKeyListeners(); this.addMouseListeners(); this.addKeyboardEvents(); this.addBodyViewportListener(); this.addStopEditingWhenGridLosesFocus(); this.mockContextMenuForIPad(); this.addRowDragListener(); if (this.$scope) { this.addAngularApplyCheck(); } this.onDisplayedColumnsWidthChanged(); // this.addWindowResizeListener(); this.gridApi.registerGridComp(this); this.alignedGridsService.registerGridComp(this); this.headerRootComp.registerGridComp(this); this.animationFrameService.registerGridComp(this); this.navigationService.registerGridComp(this); this.heightScaler.registerGridComp(this); this.autoHeightCalculator.registerGridComp(this); this.columnAnimationService.registerGridComp(this); this.autoWidthCalculator.registerGridComp(this); this.paginationAutoPageSizeService.registerGridComp(this); this.beans.registerGridComp(this); this.rowRenderer.registerGridComp(this); if (this.rangeController) { this.rangeController.registerGridComp(this); } var unsubscribeFromResize = resizeObserver_1.observeResize(this.eBodyViewport, this.onBodyViewportResized.bind(this)); this.addDestroyFunc(function () { return unsubscribeFromResize(); }); }; GridPanel.prototype.onBodyViewportResized = function () { this.checkViewportAndScrolls(); }; // used by ColumnAnimationService GridPanel.prototype.setColumnMovingCss = function (moving) { this.addOrRemoveCssClass('ag-column-moving', moving); }; GridPanel.prototype.setupOverlay = function () { this.overlayWrapper = this.componentRecipes.newOverlayWrapperComponent(); this.eOverlay = this.queryForHtmlElement('[ref="eOverlay"]'); this.overlayWrapper.hideOverlay(this.eOverlay); }; GridPanel.prototype.addRowDragListener = function () { var rowDragFeature = new rowDragFeature_1.RowDragFeature(this.eBody, this); this.context.wireBean(rowDragFeature); this.dragAndDropService.addDropTarget(rowDragFeature); }; GridPanel.prototype.addStopEditingWhenGridLosesFocus = function () { var _this = this; if (this.gridOptionsWrapper.isStopEditingWhenGridLosesFocus()) { this.addDestroyableEventListener(this.eBody, 'focusout', function (event) { // this is the element the focus is moving to var elementWithFocus = event.relatedTarget; // see if the element the focus is going to is part of the grid var clickInsideGrid = false; var pointer = elementWithFocus; while (utils_1.Utils.exists(pointer) && !clickInsideGrid) { var isPopup = !!_this.gridOptionsWrapper.getDomData(pointer, popupEditorWrapper_1.PopupEditorWrapper.DOM_KEY_POPUP_EDITOR_WRAPPER); var isBody = _this.eBody == pointer; clickInsideGrid = isPopup || isBody; pointer = pointer.parentNode; } if (!clickInsideGrid) { _this.rowRenderer.stopEditing(); } }); } }; GridPanel.prototype.addAngularApplyCheck = function () { var _this = this; // this makes sure if we queue up requests, we only execute oe var applyTriggered = false; var listener = function () { // only need to do one apply at a time if (applyTriggered) { return; } applyTriggered = true; // mark 'need apply' to true setTimeout(function () { applyTriggered = false; _this.$scope.$apply(); }, 0); }; // these are the events we need to do an apply after - these are the ones that can end up // with columns added or removed this.addDestroyableEventListener(this.eventService, events_1.Events.EVENT_DISPLAYED_COLUMNS_CHANGED, listener); this.addDestroyableEventListener(this.eventService, events_1.Events.EVENT_VIRTUAL_COLUMNS_CHANGED, listener); }; // if we do not do this, then the user can select a pic in the grid (eg an image in a custom cell renderer) // and then that will start the browser native drag n' drop, which messes up with our own drag and drop. GridPanel.prototype.disableBrowserDragging = function () { this.getGui().addEventListener('dragstart', function (event) { if (event.target instanceof HTMLImageElement) { event.preventDefault(); return false; } }); }; GridPanel.prototype.addEventListeners = function () { this.addDestroyableEventListener(this.eventService, events_1.Events.EVENT_DISPLAYED_COLUMNS_CHANGED, this.onDisplayedColumnsChanged.bind(this)); this.addDestroyableEventListener(this.eventService, events_1.Events.EVENT_DISPLAYED_COLUMNS_WIDTH_CHANGED, this.onDisplayedColumnsWidthChanged.bind(this)); this.addDestroyableEventListener(this.eventService, events_1.Events.EVENT_PINNED_ROW_DATA_CHANGED, this.setBodyAndHeaderHeights.bind(this)); this.addDestroyableEventListener(this.eventService, events_1.Events.EVENT_ROW_DATA_CHANGED, this.onRowDataChanged.bind(this)); this.addDestroyableEventListener(this.eventService, events_1.Events.EVENT_ROW_DATA_UPDATED, this.onRowDataChanged.bind(this)); this.addDestroyableEventListener(this.eventService, events_1.Events.EVENT_NEW_COLUMNS_LOADED, this.onNewColumnsLoaded.bind(this)); this.addDestroyableEventListener(this.gridOptionsWrapper, gridOptionsWrapper_1.GridOptionsWrapper.PROP_HEADER_HEIGHT, this.setBodyAndHeaderHeights.bind(this)); this.addDestroyableEventListener(this.gridOptionsWrapper, gridOptionsWrapper_1.GridOptionsWrapper.PROP_PIVOT_HEADER_HEIGHT, this.setBodyAndHeaderHeights.bind(this)); this.addDestroyableEventListener(this.gridOptionsWrapper, gridOptionsWrapper_1.GridOptionsWrapper.PROP_GROUP_HEADER_HEIGHT, this.setBodyAndHeaderHeights.bind(this)); this.addDestroyableEventListener(this.gridOptionsWrapper, gridOptionsWrapper_1.GridOptionsWrapper.PROP_PIVOT_GROUP_HEADER_HEIGHT, this.setBodyAndHeaderHeights.bind(this)); this.addDestroyableEventListener(this.gridOptionsWrapper, gridOptionsWrapper_1.GridOptionsWrapper.PROP_FLOATING_FILTERS_HEIGHT, this.setBodyAndHeaderHeights.bind(this)); }; GridPanel.prototype.addDragListeners = function () { var _this = this; if (!this.gridOptionsWrapper.isEnableRangeSelection() // no range selection if no property || utils_1.Utils.missing(this.rangeController)) { return; } var containers = [this.eLeftContainer, this.eRightContainer, this.eBodyContainer, this.eTop, this.eBottom]; containers.forEach(function (container) { var params = { eElement: container, onDragStart: _this.rangeController.onDragStart.bind(_this.rangeController), onDragStop: _this.rangeController.onDragStop.bind(_this.rangeController), onDragging: _this.rangeController.onDragging.bind(_this.rangeController), // for range selection by dragging the mouse, we want to ignore the event if shift key is pressed, // as shift key click is another type of range selection skipMouseEvent: function (mouseEvent) { return mouseEvent.shiftKey; } }; _this.dragService.addDragSource(params); _this.addDestroyFunc(function () { return _this.dragService.removeDragSource(params); }); }); }; GridPanel.prototype.addMouseListeners = function () { var _this = this; var eventNames = ['click', 'mousedown', 'dblclick', 'contextmenu', 'mouseover', 'mouseout']; eventNames.forEach(function (eventName) { var listener = _this.processMouseEvent.bind(_this, eventName); _this.eAllCellContainers.forEach(function (container) { return _this.addDestroyableEventListener(container, eventName, listener); }); }); }; GridPanel.prototype.addKeyboardEvents = function () { var _this = this; var eventNames = ['keydown', 'keypress']; eventNames.forEach(function (eventName) { var listener = _this.processKeyboardEvent.bind(_this, eventName); _this.eAllCellContainers.forEach(function (container) { _this.addDestroyableEventListener(container, eventName, listener); }); }); }; GridPanel.prototype.addBodyViewportListener = function () { var _this = this; // we want to listen for clicks directly on the eBodyViewport, so the user has a way of showing // the context menu if no rows are displayed, or user simply clicks outside of a cell var listener = function (mouseEvent) { var target = utils_1.Utils.getTarget(mouseEvent); if (target === _this.eBodyViewport || target === _this.eLeftViewport || target === _this.eRightViewport) { // show it _this.onContextMenu(mouseEvent, null, null, null, null); _this.preventDefaultOnContextMenu(mouseEvent); } }; //For some reason listening only to this.eBody doesnt work... Maybe because the event is consumed somewhere else? //In any case, not expending much time on this, if anyome comes accross this and knows how to make this work with //one listener please go ahead and change it... this.addDestroyableEventListener(this.eBodyViewport, 'contextmenu', listener); this.addDestroyableEventListener(this.eRightViewport, 'contextmenu', listener); this.addDestroyableEventListener(this.eLeftViewport, 'contextmenu', listener); }; // + rangeController GridPanel.prototype.getBodyClientRect = function () { if (this.eBody) { return this.eBody.getBoundingClientRect(); } }; GridPanel.prototype.getRowForEvent = function (event) { var sourceElement = utils_1.Utils.getTarget(event); while (sourceElement) { var renderedRow = this.gridOptionsWrapper.getDomData(sourceElement, rowComp_1.RowComp.DOM_DATA_KEY_RENDERED_ROW); if (renderedRow) { return renderedRow; } sourceElement = sourceElement.parentElement; } return null; }; GridPanel.prototype.processKeyboardEvent = function (eventName, keyboardEvent) { var renderedCell = this.mouseEventService.getRenderedCellForEvent(keyboardEvent); if (!renderedCell) { return; } switch (eventName) { case 'keydown': // first see if it's a scroll key, page up / down, home / end etc var wasScrollKey = this.navigationService.handlePageScrollingKey(keyboardEvent); // if not a scroll key, then we pass onto cell if (!wasScrollKey) { renderedCell.onKeyDown(keyboardEvent); } break; case 'keypress': renderedCell.onKeyPress(keyboardEvent); break; } }; // gets called by rowRenderer when new data loaded, as it will want to scroll to the top GridPanel.prototype.scrollToTop = function () { this.eBodyViewport.scrollTop = 0; }; GridPanel.prototype.processMouseEvent = function (eventName, mouseEvent) { if (!this.mouseEventService.isEventFromThisGrid(mouseEvent)) { return; } if (utils_1.Utils.isStopPropagationForAgGrid(mouseEvent)) { return; } var rowComp = this.getRowForEvent(mouseEvent); var cellComp = this.mouseEventService.getRenderedCellForEvent(mouseEvent); if (eventName === "contextmenu") { this.handleContextMenuMouseEvent(mouseEvent, null, rowComp, cellComp); } else { if (cellComp) { cellComp.onMouseEvent(eventName, mouseEvent); } if (rowComp) { rowComp.onMouseEvent(eventName, mouseEvent); } } this.preventDefaultOnContextMenu(mouseEvent); }; GridPanel.prototype.mockContextMenuForIPad = function () { var _this = this; // we do NOT want this when not in ipad, otherwise we will be doing if (!utils_1.Utils.isUserAgentIPad()) { return; } this.eAllCellContainers.forEach(function (container) { var touchListener = new touchListener_1.TouchListener(container); var longTapListener = function (event) { var rowComp = _this.getRowForEvent(event.touchEvent); var cellComp = _this.mouseEventService.getRenderedCellForEvent(event.touchEvent); _this.handleContextMenuMouseEvent(null, event.touchEvent, rowComp, cellComp); }; _this.addDestroyableEventListener(touchListener, touchListener_1.TouchListener.EVENT_LONG_TAP, longTapListener); _this.addDestroyFunc(function () { return touchListener.destroy(); }); }); }; GridPanel.prototype.handleContextMenuMouseEvent = function (mouseEvent, touchEvent, rowComp, cellComp) { var rowNode = rowComp ? rowComp.getRowNode() : null; var column = cellComp ? cellComp.getColumn() : null; var value = null; if (column) { var event_1 = mouseEvent ? mouseEvent : touchEvent; cellComp.dispatchCellContextMenuEvent(event_1); value = this.valueService.getValue(column, rowNode); } this.onContextMenu(mouseEvent, touchEvent, rowNode, column, value); }; GridPanel.prototype.onContextMenu = function (mouseEvent, touchEvent, rowNode, column, value) { // to allow us to debug in chrome, we ignore the event if ctrl is pressed. // not everyone wants this, so first 'if' below allows to turn this hack off. if (!this.gridOptionsWrapper.isAllowContextMenuWithControlKey()) { // then do the check if (mouseEvent && (mouseEvent.ctrlKey || mouseEvent.metaKey)) { return; } } if (this.contextMenuFactory && !this.gridOptionsWrapper.isSuppressContextMenu()) { var eventOrTouch = mouseEvent ? mouseEvent : touchEvent.touches[0]; this.contextMenuFactory.showMenu(rowNode, column, value, eventOrTouch); var event_2 = mouseEvent ? mouseEvent : touchEvent; event_2.preventDefault(); } }; GridPanel.prototype.preventDefaultOnContextMenu = function (mouseEvent) { // if we don't do this, then middle click will never result in a 'click' event, as 'mousedown' // will be consumed by the browser to mean 'scroll' (as you can scroll with the middle mouse // button in the browser). so this property allows the user to receive middle button clicks if // they want. if (this.gridOptionsWrapper.isSuppressMiddleClickScrolls() && mouseEvent.which === 2) { mouseEvent.preventDefault(); } }; GridPanel.prototype.addShortcutKeyListeners = function () { var _this = this; this.eAllCellContainers.forEach(function (container) { container.addEventListener('keydown', function (event) { // if the cell the event came from is editing, then we do not // want to do the default shortcut keys, otherwise the editor // (eg a text field) would not be able to do the normal cut/copy/paste var renderedCell = _this.mouseEventService.getRenderedCellForEvent(event); if (renderedCell && renderedCell.isEditing()) { return; } // for copy / paste, we don't want to execute when the event // was from a child grid (happens in master detail) if (!_this.mouseEventService.isEventFromThisGrid(event)) { return; } if (event.ctrlKey || event.metaKey) { switch (event.which) { case constants_1.Constants.KEY_A: return _this.onCtrlAndA(event); case constants_1.Constants.KEY_C: return _this.onCtrlAndC(event); case constants_1.Constants.KEY_V: return _this.onCtrlAndV(event); case constants_1.Constants.KEY_D: return _this.onCtrlAndD(event); } } }); }); }; GridPanel.prototype.onCtrlAndA = function (event) { if (this.rangeController && this.paginationProxy.isRowsToRender()) { var rowEnd = void 0; var floatingStart = void 0; var floatingEnd = void 0; if (this.pinnedRowModel.isEmpty(constants_1.Constants.PINNED_TOP)) { floatingStart = null; } else { floatingStart = constants_1.Constants.PINNED_TOP; } if (this.pinnedRowModel.isEmpty(constants_1.Constants.PINNED_BOTTOM)) { floatingEnd = null; rowEnd = this.paginationProxy.getTotalRowCount() - 1; } else { floatingEnd = constants_1.Constants.PINNED_BOTTOM; rowEnd = this.pinnedRowModel.getPinnedBottomRowData().length - 1; } var allDisplayedColumns = this.columnController.getAllDisplayedColumns(); if (utils_1.Utils.missingOrEmpty(allDisplayedColumns)) { return; } this.rangeController.setRange({ rowStart: 0, floatingStart: floatingStart, rowEnd: rowEnd, floatingEnd: floatingEnd, columnStart: allDisplayedColumns[0], columnEnd: allDisplayedColumns[allDisplayedColumns.length - 1] }); } event.preventDefault(); return false; }; GridPanel.prototype.onCtrlAndC = function (event) { if (!this.clipboardService) { return; } var focusedCell = this.focusedCellController.getFocusedCell(); this.clipboardService.copyToClipboard(); event.preventDefault(); // the copy operation results in loosing focus on the cell, // because of the trickery the copy logic uses with a temporary // widget. so we set it back again. if (focusedCell) { this.focusedCellController.setFocusedCell(focusedCell.rowIndex, focusedCell.column, focusedCell.floating, true); } return false; }; GridPanel.prototype.onCtrlAndV = function (event) { if (!this.enterprise) { return; } if (this.gridOptionsWrapper.isSuppressClipboardPaste()) { return; } this.clipboardService.pasteFromClipboard(); return false; }; GridPanel.prototype.onCtrlAndD = function (event) { if (!this.enterprise) { return; } this.clipboardService.copyRangeDown(); event.preventDefault(); return false; }; // Valid values for position are bottom, middle and top // position should be {'top','middle','bottom', or undefined/null}. // if undefined/null, then the grid will to the minimal amount of scrolling, // eg if grid needs to scroll up, it scrolls until row is on top, // if grid needs to scroll down, it scrolls until row is on bottom, // if row is already in view, grid does not scroll GridPanel.prototype.ensureIndexVisible = function (index, position) { // if for print or auto height, everything is always visible if (this.gridOptionsWrapper.isGridAutoHeight()) { return; } var rowCount = this.paginationProxy.getTotalRowCount(); if (typeof index !== 'number' || index < 0 || index >= rowCount) { console.warn('invalid row index for ensureIndexVisible: ' + index); return; } this.paginationProxy.goToPageWithIndex(index); var rowNode = this.paginationProxy.getRow(index); var paginationOffset = this.paginationProxy.getPixelOffset(); var rowTopPixel = rowNode.rowTop - paginationOffset; var rowBottomPixel = rowTopPixel + rowNode.rowHeight; var scrollPosition = this.getVScrollPosition(); var heightOffset = this.heightScaler.getOffset(); var vScrollTop = scrollPosition.top + heightOffset; var vScrollBottom = scrollPosition.bottom + heightOffset; if (this.isHorizontalScrollShowing()) { vScrollBottom -= this.scrollWidth; } var viewportHeight = vScrollBottom - vScrollTop; var newScrollPosition = null; // work out the pixels for top, middle and bottom up front, // make the if/else below easier to read var pxTop = this.heightScaler.getScrollPositionForPixel(rowTopPixel); var pxBottom = this.heightScaler.getScrollPositionForPixel(rowBottomPixel - viewportHeight); var pxMiddle = (pxTop + pxBottom) / 2; // make sure if middle, the row is not outside the top of the grid if (pxMiddle > rowTopPixel) { pxMiddle = rowTopPixel; } var rowBelowViewport = vScrollTop > rowTopPixel; var rowAboveViewport = vScrollBottom < rowBottomPixel; if (position === 'top') { newScrollPosition = pxTop; } else if (position === 'bottom') { newScrollPosition = pxBottom; } else if (position === 'middle') { newScrollPosition = pxMiddle; } else if (rowBelowViewport) { // if row is before, scroll up with row at top newScrollPosition = pxTop; } else if (rowAboveViewport) { // if row is below, scroll down with row at bottom newScrollPosition = pxBottom; } if (newScrollPosition !== null) { this.eBodyViewport.scrollTop = newScrollPosition; this.rowRenderer.redrawAfterScroll(); } }; // + moveColumnController GridPanel.prototype.getCenterWidth = function () { return this.eBodyViewport.clientWidth; }; GridPanel.prototype.isHorizontalScrollShowing = function () { return utils_1.Utils.isHorizontalScrollShowing(this.eBodyViewport); }; GridPanel.prototype.isVerticalScrollShowing = function () { return utils_1.Utils.isVerticalScrollShowing(this.eBodyViewport); }; // gets called every time the viewport size changes. we use this to check visibility of scrollbars // in the grid panel, and also to check size and position of viewport for row and column virtualisation. GridPanel.prototype.checkViewportAndScrolls = function () { // results in updating anything that depends on scroll showing this.updateScrollVisibleService(); // fires event if height changes, used by PaginationService, HeightScalerService, RowRenderer this.checkBodyHeight(); // check for virtual columns for ColumnController this.onHorizontalViewportChanged(); this.setPinnedLeftWidth(); this.setPinnedRightWidth(); this.setBottomPaddingOnPinned(); this.hideVerticalScrollOnCenter(); this.hideFullWidthViewportScrollbars(); }; GridPanel.prototype.updateScrollVisibleService = function () { var params = { bodyHorizontalScrollShowing: false, leftVerticalScrollShowing: false, rightVerticalScrollShowing: false }; if (this.enableRtl && this.columnController.isPinningLeft()) { params.leftVerticalScrollShowing = utils_1.Utils.isVerticalScrollShowing(this.eLeftViewport); } if (!this.enableRtl && this.columnController.isPinningRight()) { params.rightVerticalScrollShowing = utils_1.Utils.isVerticalScrollShowing(this.eRightViewport); } params.bodyHorizontalScrollShowing = this.isHorizontalScrollShowing(); this.scrollVisibleService.setScrollsVisible(params); }; // the pinned container needs extra space at the bottom, some blank space, otherwise when // vertically scrolled all the way down, the last row will be hidden behind the scrolls. // this extra padding allows the last row to be lifted above the bottom scrollbar. GridPanel.prototype.setBottomPaddingOnPinned = function () { // no need for padding if the scrollbars are not taking up any space if (this.scrollWidth <= 0) { return; } if (this.isHorizontalScrollShowing()) { this.eRightContainer.style.marginBottom = this.scrollWidth + 'px'; this.eLeftContainer.style.marginBottom = this.scrollWidth + 'px'; } else { this.eRightContainer.style.marginBottom = ''; this.eLeftContainer.style.marginBottom = ''; } }; GridPanel.prototype.hideFullWidthViewportScrollbars = function () { // if browser does not have scrollbars that take up space (eg iOS) then we don't need // to adjust the sizes of the container for scrollbars // if (this.scrollWidth <= 0) { return; } var scrollWidthPx = this.scrollClipWidth > 0 ? this.scrollWidth + 'px' : ''; // if horizontal scroll is showing, we add padding to bottom so // fullWidth container is not spreading over the scroll this.eFullWidthViewportWrapper.style.paddingBottom = this.isHorizontalScrollShowing() ? scrollWidthPx : ''; // if vertical scroll is showing on full width viewport, then we clip it away, otherwise // it competes with the main vertical scroll. this is done by getting the viewport to be // bigger than the wrapper, the wrapper then ends up clipping the viewport. var takeOutVScroll = this.isVerticalScrollShowing(); if (this.enableRtl) { this.eFullWidthViewportWrapper.style.marginLeft = takeOutVScroll ? scrollWidthPx : ''; this.eFullWidthViewport.style.marginLeft = takeOutVScroll ? ('-' + scrollWidthPx) : ''; } else { this.eFullWidthViewportWrapper.style.width = takeOutVScroll ? "calc(100% - " + scrollWidthPx + ")" : ''; this.eFullWidthViewport.style.width = takeOutVScroll ? "calc(100% + " + scrollWidthPx + ")" : ''; } }; GridPanel.prototype.ensureColumnVisible = function (key) { var column = this.columnController.getGridColumn(key); if (!column) { return; } if (column.isPinned()) { console.warn('calling ensureIndexVisible on a ' + column.getPinned() + ' pinned column doesn\'t make sense for column ' + column.getColId()); return; } if (!this.columnController.isColumnDisplayed(column)) { console.warn('column is not currently visible'); return; } var colLeftPixel = column.getLeft(); var colRightPixel = colLeftPixel + column.getActualWidth(); var viewportWidth = this.eBodyViewport.clientWidth; var scrollPosition = this.getBodyViewportScrollLeft(); var bodyWidth = this.columnController.getBodyContainerWidth(); var viewportLeftPixel; var viewportRightPixel; // the logic of working out left and right viewport px is both here and in the ColumnController, // need to refactor it out to one place if (this.enableRtl) { viewportLeftPixel = bodyWidth - scrollPosition - viewportWidth; viewportRightPixel = bodyWidth - scrollPosition; } else { viewportLeftPixel = scrollPosition; viewportRightPixel = viewportWidth + scrollPosition; } var viewportScrolledPastCol = viewportLeftPixel > colLeftPixel; var viewportScrolledBeforeCol = viewportRightPixel < colRightPixel; var colToSmallForViewport = viewportWidth < column.getActualWidth(); var alignColToLeft = viewportScrolledPastCol || colToSmallForViewport; var alignColToRight = viewportScrolledBeforeCol; if (alignColToLeft) { // if viewport's left side is after col's left side, scroll left to pull col into viewport at left if (this.enableRtl) { var newScrollPosition = bodyWidth - viewportWidth - colLeftPixel; this.setBodyViewportScrollLeft(newScrollPosition); } else { this.setBodyViewportScrollLeft(colLeftPixel); } } else if (alignColToRight) { // if viewport's right side is before col's right side, scroll right to pull col into viewport at right if (this.enableRtl) { var newScrollPosition = bodyWidth - colRightPixel; this.setBodyViewportScrollLeft(newScrollPosition); } else { var newScrollPosition = colRightPixel - viewportWidth; this.setBodyViewportScrollLeft(newScrollPosition); } } else { // otherwise, col is already in view, so do nothing } // this will happen anyway, as the move will cause a 'scroll' event on the body, however // it is possible that the ensureColumnVisible method is called from within ag-Grid and // the caller will need to have the columns rendered to continue, which will be before // the event has been worked on (which is the case for cell navigation). this.onHorizontalViewportChanged(); }; GridPanel.prototype.showLoadingOverlay = function () { if (!this.gridOptionsWrapper.isSuppressLoadingOverlay()) { this.overlayWrapper.showLoadingOverlay(this.eOverlay); } }; GridPanel.prototype.showNoRowsOverlay = function () { if (!this.gridOptionsWrapper.isSuppressNoRowsOverlay()) { this.overlayWrapper.showNoRowsOverlay(this.eOverlay); } }; GridPanel.prototype.hideOverlay = function () { this.overlayWrapper.hideOverlay(this.eOverlay); }; GridPanel.prototype.getWidthForSizeColsToFit = function () { var availableWidth = this.eBody.clientWidth; // if pinning right, then the scroll bar can show, however for some reason // it overlays the grid and doesn't take space. so we are only interested // in the body scroll showing. var removeVerticalScrollWidth = this.isVerticalScrollShowing(); if (removeVerticalScrollWidth) { availableWidth -= this.scrollWidth; } return availableWidth; }; // method will call itself if no available width. this covers if the grid // isn't visible, but is just about to be visible. GridPanel.prototype.sizeColumnsToFit = function (nextTimeout) { var _this = this; var availableWidth = this.getWidthForSizeColsToFit(); if (availableWidth > 0) { this.columnController.sizeColumnsToFit(availableWidth, "sizeColumnsToFit"); } else { if (nextTimeout === undefined) { setTimeout(function () { _this.sizeColumnsToFit(100); }, 0); } else if (nextTimeout === 100) { setTimeout(function () { _this.sizeColumnsToFit(500); }, 100); } else if (nextTimeout === 500) { setTimeout(function () { _this.sizeColumnsToFit(-1); }, 500); } else { console.log('ag-Grid: tried to call sizeColumnsToFit() but the grid is coming back with ' + 'zero width, maybe the grid is not visible yet on the screen?'); } } }; GridPanel.prototype.getBodyContainer = function () { return this.eBodyContainer; }; GridPanel.prototype.getDropTargetBodyContainers = function () { return [this.eBodyViewport, this.eTopViewport, this.eBottomViewport]; }; GridPanel.prototype.getDropTargetLeftContainers = function () { return [this.eLeftViewport, this.eLeftBottom, this.eLeftTop]; }; GridPanel.prototype.getDropTargetRightContainers = function () { return [this.eRightViewport, this.eRightBottom, this.eRightTop]; }; GridPanel.prototype.buildRowContainerComponents = function () { var _this = this; this.eAllCellContainers = [ this.eLeftContainer, this.eRightContainer, this.eBodyContainer, this.eTop, this.eBottom, this.eFullWidthContainer ]; this.rowContainerComponents = { body: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eBodyContainer, eViewport: this.eBodyViewport }), fullWidth: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eFullWidthContainer, hideWhenNoChildren: true, eViewport: this.eFullWidthViewport }), pinnedLeft: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eLeftContainer, eViewport: this.eLeftViewport }), pinnedRight: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eRightContainer, eViewport: this.eRightViewport }), floatingTop: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eTopContainer }), floatingTopPinnedLeft: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eLeftTop }), floatingTopPinnedRight: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eRightTop }), floatingTopFullWidth: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eTopFullWidthContainer, hideWhenNoChildren: true }), floatingBottom: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eBottomContainer }), floatingBottomPinnedLeft: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eLeftBottom }), floatingBottomPinnedRight: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eRightBottom }), floatingBottomFullWith: new rowContainerComponent_1.RowContainerComponent({ eContainer: this.eBottomFullWidthContainer, hideWhenNoChildren: true }), }; utils_1.Utils.iterateObject(this.rowContainerComponents, function (key, container) { if (container) { _this.context.wireBean(container); } }); }; GridPanel.prototype.setupRowAnimationCssClass = function () { var _this = this; var listener = function () { // we don't want to use row animation if scaling, as rows jump strangely as you scroll, // when scaling and doing row animation. var animateRows = _this.gridOptionsWrapper.isAnimateRows() && !_this.heightScaler.isScaling(); utils_1.Utils.addOrRemoveCssClass(_this.eBody, 'ag-row-animation', animateRows); utils_1.Utils.addOrRemoveCssClass(_this.eBody, 'ag-row-no-animation', !animateRows); }; listener(); this.addDestroyableEventListener(this.eventService, events_1.Events.EVENT_HEIGHT_SCALE_CHANGED, listener); }; // when editing a pinned row, if the cell is half outside the scrollable area, the browser can // scroll the column into view. we do not want this, the pinned sections should never scroll. // so we listen to scrolls on these containers and reset the scroll if we find one. GridPanel.prototype.suppressScrollOnFloatingRow = function () { var _this = this; var resetTopScroll = function () { return _this.eTopViewport.scrollLeft = 0; }; var resetBottomScroll = function () { return _this.eTopViewport.scrollLeft = 0; }; this.addDestroyableEventListener(this.eTopViewport, 'scroll', resetTopScroll); this.addDestroyableEventListener(this.eBottomViewport, 'scroll', resetBottomScroll); }; GridPanel.prototype.getRowContainers = function () { return this.rowContainerComponents; }; GridPanel.prototype.onDisplayedColumnsChanged = function () { this.setPinnedContainersVisible(); this.setBodyAndHeaderHeights(); this.onHorizontalViewportChanged(); }; GridPanel.prototype.onDisplayedColumnsWidthChanged = function () { this.setWidthsOfContainers(); this.onHorizontalViewportChanged(); if (this.enableRtl) { // because RTL is all backwards, a change in the width of the row // can cause a change in the scroll position, without a scroll event, // because the scroll position in RTL is a function that depends on // the width. to be convinced of this, take out this line, enable RTL, // scroll all the way to the left and then resize a column this.horizontallyScrollHeaderCenterAndFloatingCenter(); } }; GridPanel.prototype.setWidthsOfContainers = function () { this.setCenterWidth(); this.setPinnedLeftWidth(); this.setPinnedRightWidth(); }; GridPanel.prototype.setCenterWidth = function () { var widthPx = this.columnController.getBodyContainerWidth() + 'px'; this.eBodyContainer.style.width = widthPx; this.eBottomContainer.style.width = widthPx; this.eTopContainer.style.width = widthPx; }; GridPanel.prototype.setPinnedLeftWidth = function () { var widthOfCols = this.columnController.getPinnedLeftContainerWidth(); var widthOfColsAndScroll = widthOfCols + this.scrollWidth; var widthOfColsAndClippedScroll = widthOfCols + this.scrollClipWidth; var viewportWidth; var wrapperWidth; if (utils_1.Utils.isVerticalScrollShowing(this.eLeftViewport)) { if (this.enableRtl) { // show the scroll viewportWidth = widthOfColsAndScroll; wrapperWidth = widthOfColsAndScroll; } else { // hide the scroll viewportWidth = widthOfColsAndClippedScroll; wrapperWidth = widthOfCols; } } else { // no scroll viewportWidth = widthOfCols; wrapperWidth = widthOfCols; } this.setElementWidth(this.eLeftViewportWrapper, wrapperWidth); this.setElementWidth(this.eLeftViewport, view