ag-grid
Version:
Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
1,039 lines • 70.8 kB
JavaScript
/**
* 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 __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var utils_1 = require("../utils");
var column_1 = require("../entities/column");
var rowNode_1 = require("../entities/rowNode");
var constants_1 = require("../constants");
var events_1 = require("../events");
var gridCell_1 = require("../entities/gridCell");
var component_1 = require("../widgets/component");
var checkboxSelectionComponent_1 = require("./checkboxSelectionComponent");
var rowDragComp_1 = require("./rowDragComp");
var CellComp = (function (_super) {
__extends(CellComp, _super);
function CellComp(scope, beans, column, rowNode, rowComp, autoHeightCell) {
var _this = _super.call(this) || this;
_this.editingCell = false;
// every time we go into edit mode, or back again, this gets incremented.
// it's the components way of dealing with the async nature of framework components,
// so if a framework component takes a while to be created, we know if the object
// is still relevant when creating is finished. eg we could click edit / unedit 20
// times before the first React edit component comes back - we should discard
// the first 19.
_this.cellEditorVersion = 0;
_this.cellRendererVersion = 0;
_this.scope = scope;
_this.beans = beans;
_this.column = column;
_this.rowNode = rowNode;
_this.rowComp = rowComp;
_this.autoHeightCell = autoHeightCell;
_this.createGridCellVo();
_this.rangeSelectionEnabled = beans.enterprise && beans.gridOptionsWrapper.isEnableRangeSelection();
_this.cellFocused = _this.beans.focusedCellController.isCellFocused(_this.gridCell);
_this.firstRightPinned = _this.column.isFirstRightPinned();
_this.lastLeftPinned = _this.column.isLastLeftPinned();
if (_this.rangeSelectionEnabled) {
_this.rangeCount = _this.beans.rangeController.getCellRangeCount(_this.gridCell);
}
_this.getValueAndFormat();
_this.setUsingWrapper();
_this.chooseCellRenderer();
_this.setupColSpan();
_this.rowSpan = _this.column.getRowSpan(_this.rowNode);
return _this;
}
CellComp.prototype.getCreateTemplate = function () {
var templateParts = [];
var col = this.column;
var width = this.getCellWidth();
var left = col.getLeft();
var valueToRender = this.getInitialValueToRender();
var valueSanitised = utils_1._.get(this.column, 'colDef.template', null) ? valueToRender : utils_1._.escape(valueToRender);
this.tooltip = this.getToolTip();
var tooltipSanitised = utils_1._.escape(this.tooltip);
var colIdSanitised = utils_1._.escape(col.getId());
var wrapperStartTemplate;
var wrapperEndTemplate;
var stylesFromColDef = this.preProcessStylesFromColDef();
var cssClasses = this.getInitialCssClasses();
var stylesForRowSpanning = this.getStylesForRowSpanning();
if (this.usingWrapper) {
wrapperStartTemplate = '<span ref="eCellWrapper" class="ag-cell-wrapper"><span ref="eCellValue" class="ag-cell-value">';
wrapperEndTemplate = '</span></span>';
}
// hey, this looks like React!!!
templateParts.push("<div");
templateParts.push(" tabindex=\"-1\"");
templateParts.push(" role=\"gridcell\"");
templateParts.push(" comp-id=\"" + this.getCompId() + "\" ");
templateParts.push(" col-id=\"" + colIdSanitised + "\"");
templateParts.push(" class=\"" + cssClasses.join(' ') + "\"");
templateParts.push(tooltipSanitised ? " title=\"" + tooltipSanitised + "\"" : "");
templateParts.push(" style=\"width: " + width + "px; left: " + left + "px; " + stylesFromColDef + " " + stylesForRowSpanning + "\" >");
templateParts.push(wrapperStartTemplate);
templateParts.push(valueSanitised);
templateParts.push(wrapperEndTemplate);
templateParts.push("</div>");
return templateParts.join('');
};
CellComp.prototype.getStylesForRowSpanning = function () {
if (this.rowSpan === 1) {
return '';
}
var singleRowHeight = this.beans.gridOptionsWrapper.getRowHeightAsNumber();
var totalRowHeight = singleRowHeight * this.rowSpan;
return "height: " + totalRowHeight + "px; z-index: 1;";
};
CellComp.prototype.afterAttached = function () {
var querySelector = "[comp-id=\"" + this.getCompId() + "\"]";
var eGui = this.eParentRow.querySelector(querySelector);
this.setGui(eGui);
// all of these have dependencies on the eGui, so only do them after eGui is set
this.addDomData();
this.populateTemplate();
this.attachCellRenderer();
this.angular1Compile();
this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_CELL_FOCUSED, this.onCellFocused.bind(this));
this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_FLASH_CELLS, this.onFlashCells.bind(this));
this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_COLUMN_HOVER_CHANGED, this.onColumnHover.bind(this));
this.addDestroyableEventListener(this.rowNode, rowNode_1.RowNode.EVENT_ROW_INDEX_CHANGED, this.onRowIndexChanged.bind(this));
this.addDestroyableEventListener(this.rowNode, rowNode_1.RowNode.EVENT_CELL_CHANGED, this.onCellChanged.bind(this));
this.addDestroyableEventListener(this.column, column_1.Column.EVENT_LEFT_CHANGED, this.onLeftChanged.bind(this));
this.addDestroyableEventListener(this.column, column_1.Column.EVENT_WIDTH_CHANGED, this.onWidthChanged.bind(this));
this.addDestroyableEventListener(this.column, column_1.Column.EVENT_FIRST_RIGHT_PINNED_CHANGED, this.onFirstRightPinnedChanged.bind(this));
this.addDestroyableEventListener(this.column, column_1.Column.EVENT_LAST_LEFT_PINNED_CHANGED, this.onLastLeftPinnedChanged.bind(this));
// if not doing enterprise, then range selection service would be missing
// so need to check before trying to use it
if (this.rangeSelectionEnabled) {
this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_RANGE_SELECTION_CHANGED, this.onRangeSelectionChanged.bind(this));
}
};
CellComp.prototype.onColumnHover = function () {
var isHovered = this.beans.columnHoverService.isHovered(this.column);
utils_1._.addOrRemoveCssClass(this.getGui(), 'ag-column-hover', isHovered);
};
CellComp.prototype.onCellChanged = function (event) {
var eventImpactsThisCell = event.column === this.column;
if (eventImpactsThisCell) {
this.refreshCell({});
}
};
CellComp.prototype.getCellLeft = function () {
var mostLeftCol;
if (this.beans.gridOptionsWrapper.isEnableRtl() && this.colsSpanning) {
mostLeftCol = this.colsSpanning[this.colsSpanning.length - 1];
}
else {
mostLeftCol = this.column;
}
return mostLeftCol.getLeft();
};
CellComp.prototype.getCellWidth = function () {
if (this.colsSpanning) {
var result_1 = 0;
this.colsSpanning.forEach(function (col) { return result_1 += col.getActualWidth(); });
return result_1;
}
else {
return this.column.getActualWidth();
}
};
CellComp.prototype.onFlashCells = function (event) {
var cellId = this.gridCell.createId();
var shouldFlash = event.cells[cellId];
if (shouldFlash) {
this.animateCell('highlight');
}
};
CellComp.prototype.setupColSpan = function () {
// if no col span is active, then we don't set it up, as it would be wasteful of CPU
if (utils_1._.missing(this.column.getColDef().colSpan)) {
return;
}
// because we are col spanning, a reorder of the cols can change what cols we are spanning over
this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_DISPLAYED_COLUMNS_CHANGED, this.onDisplayColumnsChanged.bind(this));
// because we are spanning over multiple cols, we check for width any time any cols width changes.
// this is expensive - really we should be explicitly checking only the cols we are spanning over
// instead of every col, however it would be tricky code to track the cols we are spanning over, so
// because hardly anyone will be using colSpan, am favoring this easier way for more maintainable code.
this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_DISPLAYED_COLUMNS_WIDTH_CHANGED, this.onWidthChanged.bind(this));
this.colsSpanning = this.getColSpanningList();
};
CellComp.prototype.getColSpanningList = function () {
var colSpan = this.column.getColSpan(this.rowNode);
var colsSpanning = [];
// if just one col, the col span is just the column we are in
if (colSpan === 1) {
colsSpanning.push(this.column);
}
else {
var pointer = this.column;
var pinned = this.column.getPinned();
for (var i = 0; i < colSpan; i++) {
colsSpanning.push(pointer);
pointer = this.beans.columnController.getDisplayedColAfter(pointer);
if (utils_1._.missing(pointer)) {
break;
}
// we do not allow col spanning to span outside of pinned areas
if (pinned !== pointer.getPinned()) {
break;
}
}
}
return colsSpanning;
};
CellComp.prototype.onDisplayColumnsChanged = function () {
var colsSpanning = this.getColSpanningList();
if (!utils_1._.compareArrays(this.colsSpanning, colsSpanning)) {
this.colsSpanning = colsSpanning;
this.onWidthChanged();
this.onLeftChanged(); // left changes when doing RTL
}
};
CellComp.prototype.getInitialCssClasses = function () {
var cssClasses = ["ag-cell", "ag-cell-not-inline-editing"];
// if we are putting the cell into a dummy container, to work out it's height,
// then we don't put the height css in, as we want cell to fit height in that case.
if (!this.autoHeightCell) {
cssClasses.push('ag-cell-with-height');
}
var doingFocusCss = !this.beans.gridOptionsWrapper.isSuppressCellSelection();
if (doingFocusCss) {
// otherwise the class depends on the focus state
cssClasses.push(this.cellFocused ? 'ag-cell-focus' : 'ag-cell-no-focus');
}
else {
// if we are not doing cell selection, then ag-cell-no-focus gets put onto every cell
cssClasses.push('ag-cell-no-focus');
}
if (this.firstRightPinned) {
cssClasses.push('ag-cell-first-right-pinned');
}
if (this.lastLeftPinned) {
cssClasses.push('ag-cell-last-left-pinned');
}
if (this.beans.columnHoverService.isHovered(this.column)) {
cssClasses.push('ag-column-hover');
}
utils_1._.pushAll(cssClasses, this.preProcessClassesFromColDef());
utils_1._.pushAll(cssClasses, this.preProcessCellClassRules());
utils_1._.pushAll(cssClasses, this.getRangeClasses());
// if using the wrapper, this class goes on the wrapper instead
if (!this.usingWrapper) {
cssClasses.push('ag-cell-value');
}
return cssClasses;
};
CellComp.prototype.getInitialValueToRender = function () {
// if using a cellRenderer, then render the html from the cell renderer if it exists
if (this.usingCellRenderer) {
if (typeof this.cellRendererGui === 'string') {
return this.cellRendererGui;
}
else {
return '';
}
}
var colDef = this.column.getColDef();
if (colDef.template) {
// template is really only used for angular 1 - as people using ng1 are used to providing templates with
// bindings in it. in ng2, people will hopefully want to provide components, not templates.
return colDef.template;
}
else if (colDef.templateUrl) {
// likewise for templateUrl - it's for ng1 really - when we move away from ng1, we can take these out.
// niall was pro angular 1 when writing template and templateUrl, if writing from scratch now, would
// not do these, but would follow a pattern that was friendly towards components, not templates.
var template = this.beans.templateService.getTemplate(colDef.templateUrl, this.refreshCell.bind(this, true));
if (template) {
return template;
}
else {
return '';
}
}
else {
return this.getValueToUse();
}
};
CellComp.prototype.getRenderedRow = function () {
return this.rowComp;
};
CellComp.prototype.isSuppressNavigable = function () {
return this.column.isSuppressNavigable(this.rowNode);
};
CellComp.prototype.getCellRenderer = function () {
return this.cellRenderer;
};
CellComp.prototype.getCellEditor = function () {
return this.cellEditor;
};
// + stop editing {forceRefresh: true, suppressFlash: true}
// + event cellChanged {}
// + cellRenderer.params.refresh() {} -> method passes 'as is' to the cellRenderer, so params could be anything
// + rowComp: event dataChanged {animate: update, newData: !update}
// + rowComp: api refreshCells() {animate: true/false}
// + rowRenderer: api softRefreshView() {}
CellComp.prototype.refreshCell = function (params) {
if (this.editingCell) {
return;
}
var newData = params && params.newData;
var suppressFlash = (params && params.suppressFlash) || this.column.getColDef().suppressCellFlash;
var forceRefresh = params && params.forceRefresh;
var oldValue = this.value;
this.getValueAndFormat();
// for simple values only (not pojo's), see if the value is the same, and if it is, skip the refresh.
// when never allow skipping after an edit, as after editing, we need to put the GUI back to the way
// if was before the edit.
var valuesDifferent = !this.valuesAreEqual(oldValue, this.value);
var dataNeedsUpdating = forceRefresh || valuesDifferent;
if (dataNeedsUpdating) {
var cellRendererRefreshed = void 0;
// if it's 'new data', then we don't refresh the cellRenderer, even if refresh method is available.
// this is because if the whole data is new (ie we are showing stock price 'BBA' now and not 'SSD')
// then we are not showing a movement in the stock price, rather we are showing different stock.
if (newData || suppressFlash) {
cellRendererRefreshed = false;
}
else {
cellRendererRefreshed = this.attemptCellRendererRefresh();
}
// we do the replace if not doing refresh, or if refresh was unsuccessful.
// the refresh can be unsuccessful if we are using a framework (eg ng2 or react) and the framework
// wrapper has the refresh method, but the underlying component doesn't
if (!cellRendererRefreshed) {
this.replaceContentsAfterRefresh();
}
if (!suppressFlash) {
var flashCell = this.beans.gridOptionsWrapper.isEnableCellChangeFlash()
|| this.column.getColDef().enableCellChangeFlash;
if (flashCell) {
this.flashCell();
}
}
// need to check rules. note, we ignore colDef classes and styles, these are assumed to be static
this.postProcessStylesFromColDef();
this.postProcessClassesFromColDef();
}
this.refreshToolTip();
// we do cellClassRules even if the value has not changed, so that users who have rules that
// look at other parts of the row (where the other part of the row might of changed) will work.
this.postProcessCellClassRules();
};
// user can also call this via API
CellComp.prototype.flashCell = function () {
this.animateCell('data-changed');
};
CellComp.prototype.animateCell = function (cssName) {
var fullName = 'ag-cell-' + cssName;
var animationFullName = 'ag-cell-' + cssName + '-animation';
var element = this.getGui();
// we want to highlight the cells, without any animation
utils_1._.addCssClass(element, fullName);
utils_1._.removeCssClass(element, animationFullName);
// then once that is applied, we remove the highlight with animation
setTimeout(function () {
utils_1._.removeCssClass(element, fullName);
utils_1._.addCssClass(element, animationFullName);
setTimeout(function () {
// and then to leave things as we got them, we remove the animation
utils_1._.removeCssClass(element, animationFullName);
}, 1000);
}, 500);
};
CellComp.prototype.replaceContentsAfterRefresh = function () {
// otherwise we rip out the cell and replace it
utils_1._.removeAllChildren(this.eParentOfValue);
// remove old renderer component if it exists
if (this.cellRenderer && this.cellRenderer.destroy) {
this.cellRenderer.destroy();
}
this.cellRenderer = null;
this.cellRendererGui = null;
// populate
this.putDataIntoCellAfterRefresh();
this.angular1Compile();
};
CellComp.prototype.angular1Compile = function () {
// if angular compiling, then need to also compile the cell again (angular compiling sucks, please wait...)
if (this.beans.gridOptionsWrapper.isAngularCompileRows()) {
var eGui = this.getGui();
var compiledElement_1 = this.beans.$compile(eGui)(this.scope);
this.addDestroyFunc(function () {
compiledElement_1.remove();
});
}
};
CellComp.prototype.postProcessStylesFromColDef = function () {
var stylesToUse = this.processStylesFromColDef();
if (stylesToUse) {
utils_1._.addStylesToElement(this.getGui(), stylesToUse);
}
};
CellComp.prototype.preProcessStylesFromColDef = function () {
var stylesToUse = this.processStylesFromColDef();
return utils_1._.cssStyleObjectToMarkup(stylesToUse);
};
CellComp.prototype.processStylesFromColDef = function () {
var colDef = this.column.getColDef();
if (colDef.cellStyle) {
var cssToUse = void 0;
if (typeof colDef.cellStyle === 'function') {
var cellStyleParams = {
value: this.value,
data: this.rowNode.data,
node: this.rowNode,
colDef: colDef,
column: this.column,
$scope: this.scope,
context: this.beans.gridOptionsWrapper.getContext(),
api: this.beans.gridOptionsWrapper.getApi()
};
var cellStyleFunc = colDef.cellStyle;
cssToUse = cellStyleFunc(cellStyleParams);
}
else {
cssToUse = colDef.cellStyle;
}
return cssToUse;
}
};
CellComp.prototype.postProcessClassesFromColDef = function () {
var _this = this;
this.processClassesFromColDef(function (className) { return utils_1._.addCssClass(_this.getGui(), className); });
};
CellComp.prototype.preProcessClassesFromColDef = function () {
var res = [];
this.processClassesFromColDef(function (className) { return res.push(className); });
return res;
};
CellComp.prototype.processClassesFromColDef = function (onApplicableClass) {
this.beans.stylingService.processStaticCellClasses(this.column.getColDef(), {
value: this.value,
data: this.rowNode.data,
node: this.rowNode,
colDef: this.column.getColDef(),
rowIndex: this.rowNode.rowIndex,
$scope: this.scope,
api: this.beans.gridOptionsWrapper.getApi(),
context: this.beans.gridOptionsWrapper.getContext()
}, onApplicableClass);
};
CellComp.prototype.putDataIntoCellAfterRefresh = function () {
// template gets preference, then cellRenderer, then do it ourselves
var colDef = this.column.getColDef();
if (colDef.template) {
// template is really only used for angular 1 - as people using ng1 are used to providing templates with
// bindings in it. in ng2, people will hopefully want to provide components, not templates.
this.eParentOfValue.innerHTML = colDef.template;
}
else if (colDef.templateUrl) {
// likewise for templateUrl - it's for ng1 really - when we move away from ng1, we can take these out.
// niall was pro angular 1 when writing template and templateUrl, if writing from scratch now, would
// not do these, but would follow a pattern that was friendly towards components, not templates.
var template = this.beans.templateService.getTemplate(colDef.templateUrl, this.refreshCell.bind(this, true));
if (template) {
this.eParentOfValue.innerHTML = template;
}
// use cell renderer if it exists
}
else if (this.usingCellRenderer) {
this.attachCellRenderer();
}
else {
var valueToUse = this.getValueToUse();
if (valueToUse !== null && valueToUse !== undefined) {
this.eParentOfValue.innerText = valueToUse;
}
}
};
CellComp.prototype.attemptCellRendererRefresh = function () {
if (utils_1._.missing(this.cellRenderer) || utils_1._.missing(this.cellRenderer.refresh)) {
return false;
}
// if the cell renderer has a refresh method, we call this instead of doing a refresh
// note: should pass in params here instead of value?? so that client has formattedValue
var params = this.createCellRendererParams();
var result = this.cellRenderer.refresh(params);
// NOTE on undefined: previous version of the cellRenderer.refresh() interface
// returned nothing, if the method existed, we assumed it refreshed. so for
// backwards compatibility, we assume if method exists and returns nothing,
// that it was successful.
return result === true || result === undefined;
};
CellComp.prototype.refreshToolTip = function () {
var newTooltip = this.getToolTip();
if (this.tooltip !== newTooltip) {
this.tooltip = newTooltip;
if (utils_1._.exists(newTooltip)) {
var tooltipSanitised = utils_1._.escape(this.tooltip);
this.eParentOfValue.setAttribute('title', tooltipSanitised);
}
else {
this.eParentOfValue.removeAttribute('title');
}
}
};
CellComp.prototype.valuesAreEqual = function (val1, val2) {
// if the user provided an equals method, use that, otherwise do simple comparison
var colDef = this.column.getColDef();
var equalsMethod = colDef ? colDef.equals : null;
if (equalsMethod) {
return equalsMethod(val1, val2);
}
else {
return val1 === val2;
}
};
CellComp.prototype.getToolTip = function () {
var colDef = this.column.getColDef();
var data = this.rowNode.data;
if (colDef.tooltipField && utils_1._.exists(data)) {
return utils_1._.getValueUsingField(data, colDef.tooltipField, this.column.isTooltipFieldContainsDots());
}
else if (colDef.tooltip) {
return colDef.tooltip({
value: this.value,
valueFormatted: this.valueFormatted,
data: this.rowNode.data,
node: this.rowNode,
colDef: this.column.getColDef(),
api: this.beans.gridOptionsWrapper.getApi(),
$scope: this.scope,
context: this.beans.gridOptionsWrapper.getContext(),
rowIndex: this.gridCell.rowIndex
});
}
else {
return null;
}
};
CellComp.prototype.processCellClassRules = function (onApplicableClass, onNotApplicableClass) {
this.beans.stylingService.processClassRules(this.column.getColDef().cellClassRules, {
value: this.value,
data: this.rowNode.data,
node: this.rowNode,
colDef: this.column.getColDef(),
rowIndex: this.gridCell.rowIndex,
api: this.beans.gridOptionsWrapper.getApi(),
$scope: this.scope,
context: this.beans.gridOptionsWrapper.getContext()
}, onApplicableClass, onNotApplicableClass);
};
CellComp.prototype.postProcessCellClassRules = function () {
var _this = this;
this.processCellClassRules(function (className) {
utils_1._.addCssClass(_this.getGui(), className);
}, function (className) {
utils_1._.removeCssClass(_this.getGui(), className);
});
};
CellComp.prototype.preProcessCellClassRules = function () {
var res = [];
this.processCellClassRules(function (className) {
res.push(className);
}, function (className) {
// not catered for, if creating, no need
// to remove class as it was never there
});
return res;
};
// a wrapper is used when we are putting a selection checkbox in the cell with the value
CellComp.prototype.setUsingWrapper = function () {
var colDef = this.column.getColDef();
// never allow selection or dragging on pinned rows
if (this.rowNode.rowPinned) {
this.usingWrapper = false;
this.includeSelectionComponent = false;
this.includeRowDraggingComponent = false;
return;
}
var cbSelectionIsFunc = typeof colDef.checkboxSelection === 'function';
var rowDraggableIsFunc = typeof colDef.rowDrag === 'function';
this.includeSelectionComponent = cbSelectionIsFunc || colDef.checkboxSelection === true;
this.includeRowDraggingComponent = rowDraggableIsFunc || colDef.rowDrag === true;
this.usingWrapper = this.includeRowDraggingComponent || this.includeSelectionComponent;
};
CellComp.prototype.chooseCellRenderer = function () {
// template gets preference, then cellRenderer, then do it ourselves
var colDef = this.column.getColDef();
// templates are for ng1, ideally we wouldn't have these, they are ng1 support
// inside the core which is bad
if (colDef.template || colDef.templateUrl) {
this.usingCellRenderer = false;
return;
}
var params = this.createCellRendererParams();
var cellRenderer = this.beans.componentResolver.getComponentToUse(colDef, 'cellRenderer', params, null);
var pinnedRowCellRenderer = this.beans.componentResolver.getComponentToUse(colDef, 'pinnedRowCellRenderer', params, null);
if (pinnedRowCellRenderer && this.rowNode.rowPinned) {
this.cellRendererType = 'pinnedRowCellRenderer';
this.usingCellRenderer = true;
}
else if (cellRenderer) {
this.cellRendererType = 'cellRenderer';
this.usingCellRenderer = true;
}
else {
this.usingCellRenderer = false;
}
};
CellComp.prototype.createCellRendererInstance = function () {
var params = this.createCellRendererParams();
this.cellRendererVersion++;
var callback = this.afterCellRendererCreated.bind(this, this.cellRendererVersion);
this.beans.componentResolver.createAgGridComponent(this.column.getColDef(), params, this.cellRendererType, params, null).then(callback);
};
CellComp.prototype.afterCellRendererCreated = function (cellRendererVersion, cellRenderer) {
// see if daemon
if (!this.isAlive() || (cellRendererVersion !== this.cellRendererVersion)) {
if (cellRenderer.destroy) {
cellRenderer.destroy();
}
return;
}
this.cellRenderer = cellRenderer;
this.cellRendererGui = this.cellRenderer.getGui();
if (utils_1._.missing(this.cellRendererGui)) {
return;
}
// if async components, then it's possible the user started editing since
// this call was made
if (!this.editingCell) {
this.eParentOfValue.appendChild(this.cellRendererGui);
}
};
CellComp.prototype.attachCellRenderer = function () {
if (!this.usingCellRenderer) {
return;
}
this.createCellRendererInstance();
};
CellComp.prototype.createCellRendererParams = function () {
var _this = this;
var params = {
value: this.value,
valueFormatted: this.valueFormatted,
getValue: this.getValue.bind(this),
setValue: function (value) {
_this.beans.valueService.setValue(_this.rowNode, _this.column, value);
},
formatValue: this.formatValue.bind(this),
data: this.rowNode.data,
node: this.rowNode,
colDef: this.column.getColDef(),
column: this.column,
$scope: this.scope,
rowIndex: this.gridCell.rowIndex,
api: this.beans.gridOptionsWrapper.getApi(),
columnApi: this.beans.gridOptionsWrapper.getColumnApi(),
context: this.beans.gridOptionsWrapper.getContext(),
refreshCell: this.refreshCell.bind(this),
eGridCell: this.getGui(),
eParentOfValue: this.eParentOfValue,
// these bits are not documented anywhere, so we could drop them?
// it was in the olden days to allow user to register for when rendered
// row was removed (the row comp was removed), however now that the user
// can provide components for cells, the destroy method gets call when this
// happens so no longer need to fire event.
addRowCompListener: this.rowComp ? this.rowComp.addEventListener.bind(this.rowComp) : null,
addRenderedRowListener: function (eventType, listener) {
console.warn('ag-Grid: since ag-Grid .v11, params.addRenderedRowListener() is now params.addRowCompListener()');
if (_this.rowComp) {
_this.rowComp.addEventListener(eventType, listener);
}
}
};
return params;
};
CellComp.prototype.formatValue = function (value) {
var valueFormatted = this.beans.valueFormatterService.formatValue(this.column, this.rowNode, this.scope, value);
var valueFormattedExists = valueFormatted !== null && valueFormatted !== undefined;
return valueFormattedExists ? valueFormatted : value;
};
CellComp.prototype.getValueToUse = function () {
var valueFormattedExists = this.valueFormatted !== null && this.valueFormatted !== undefined;
return valueFormattedExists ? this.valueFormatted : this.value;
};
CellComp.prototype.getValueAndFormat = function () {
this.value = this.getValue();
this.valueFormatted = this.beans.valueFormatterService.formatValue(this.column, this.rowNode, this.scope, this.value);
};
CellComp.prototype.getValue = function () {
// if we don't check this, then the grid will render leaf groups as open even if we are not
// allowing the user to open leaf groups. confused? remember for pivot mode we don't allow
// opening leaf groups, so we have to force leafGroups to be closed in case the user expanded
// them via the API, or user user expanded them in the UI before turning on pivot mode
var lockedClosedGroup = this.rowNode.leafGroup && this.beans.columnController.isPivotMode();
var isOpenGroup = this.rowNode.group && this.rowNode.expanded && !this.rowNode.footer && !lockedClosedGroup;
if (isOpenGroup && this.beans.gridOptionsWrapper.isGroupIncludeFooter()) {
// if doing grouping and footers, we don't want to include the agg value
// in the header when the group is open
return this.beans.valueService.getValue(this.column, this.rowNode, false, true);
}
else {
return this.beans.valueService.getValue(this.column, this.rowNode);
}
};
CellComp.prototype.onMouseEvent = function (eventName, mouseEvent) {
if (utils_1._.isStopPropagationForAgGrid(mouseEvent)) {
return;
}
switch (eventName) {
case 'click':
this.onCellClicked(mouseEvent);
break;
case 'mousedown':
this.onMouseDown(mouseEvent);
break;
case 'dblclick':
this.onCellDoubleClicked(mouseEvent);
break;
case 'mouseout':
this.onMouseOut(mouseEvent);
break;
case 'mouseover':
this.onMouseOver(mouseEvent);
break;
}
};
CellComp.prototype.dispatchCellContextMenuEvent = function (event) {
var colDef = this.column.getColDef();
var cellContextMenuEvent = this.createEvent(event, events_1.Events.EVENT_CELL_CONTEXT_MENU);
this.beans.eventService.dispatchEvent(cellContextMenuEvent);
if (colDef.onCellContextMenu) {
// to make the callback async, do in a timeout
setTimeout(function () { return colDef.onCellContextMenu(cellContextMenuEvent); }, 0);
}
};
CellComp.prototype.createEvent = function (domEvent, eventType) {
var event = {
node: this.rowNode,
data: this.rowNode.data,
value: this.value,
column: this.column,
colDef: this.column.getColDef(),
context: this.beans.gridOptionsWrapper.getContext(),
api: this.beans.gridApi,
columnApi: this.beans.columnApi,
rowPinned: this.rowNode.rowPinned,
event: domEvent,
type: eventType,
rowIndex: this.rowNode.rowIndex
};
// because we are hacking in $scope for angular 1, we have to de-reference
if (this.scope) {
event.$scope = this.scope;
}
return event;
};
CellComp.prototype.onMouseOut = function (mouseEvent) {
var cellMouseOutEvent = this.createEvent(mouseEvent, events_1.Events.EVENT_CELL_MOUSE_OUT);
this.beans.eventService.dispatchEvent(cellMouseOutEvent);
this.beans.columnHoverService.clearMouseOver();
};
CellComp.prototype.onMouseOver = function (mouseEvent) {
var cellMouseOverEvent = this.createEvent(mouseEvent, events_1.Events.EVENT_CELL_MOUSE_OVER);
this.beans.eventService.dispatchEvent(cellMouseOverEvent);
this.beans.columnHoverService.setMouseOver([this.column]);
};
CellComp.prototype.onCellDoubleClicked = function (mouseEvent) {
var colDef = this.column.getColDef();
// always dispatch event to eventService
var cellDoubleClickedEvent = this.createEvent(mouseEvent, events_1.Events.EVENT_CELL_DOUBLE_CLICKED);
this.beans.eventService.dispatchEvent(cellDoubleClickedEvent);
// check if colDef also wants to handle event
if (typeof colDef.onCellDoubleClicked === 'function') {
// to make the callback async, do in a timeout
setTimeout(function () { return colDef.onCellDoubleClicked(cellDoubleClickedEvent); }, 0);
}
var editOnDoubleClick = !this.beans.gridOptionsWrapper.isSingleClickEdit()
&& !this.beans.gridOptionsWrapper.isSuppressClickEdit();
if (editOnDoubleClick) {
this.startRowOrCellEdit();
}
};
// called by rowRenderer when user navigates via tab key
CellComp.prototype.startRowOrCellEdit = function (keyPress, charPress) {
if (this.beans.gridOptionsWrapper.isFullRowEdit()) {
this.rowComp.startRowEditing(keyPress, charPress, this);
}
else {
this.startEditingIfEnabled(keyPress, charPress, true);
}
};
CellComp.prototype.isCellEditable = function () {
return this.column.isCellEditable(this.rowNode);
};
// either called internally if single cell editing, or called by rowRenderer if row editing
CellComp.prototype.startEditingIfEnabled = function (keyPress, charPress, cellStartedEdit) {
if (keyPress === void 0) { keyPress = null; }
if (charPress === void 0) { charPress = null; }
if (cellStartedEdit === void 0) { cellStartedEdit = false; }
// don't do it if not editable
if (!this.isCellEditable()) {
return;
}
// don't do it if already editing
if (this.editingCell) {
return;
}
this.editingCell = true;
this.cellEditorVersion++;
var callback = this.afterCellEditorCreated.bind(this, this.cellEditorVersion);
var params = this.createCellEditorParams(keyPress, charPress, cellStartedEdit);
this.beans.cellEditorFactory.createCellEditor(this.column.getColDef(), params).then(callback);
// if we don't do this, and editor component is async, then there will be a period
// when the component isn't present and keyboard navigation won't work - so example
// of user hitting tab quickly (more quickly than renderers getting created) won't work
var cellEditorAsync = utils_1._.missing(this.cellEditor);
if (cellEditorAsync && cellStartedEdit) {
this.focusCell(true);
}
};
CellComp.prototype.afterCellEditorCreated = function (cellEditorVersion, cellEditor) {
// if editingCell=false, means user cancelled the editor before component was ready.
// if versionMismatch, then user cancelled the edit, then started the edit again, and this
// is the first editor which is now stale.
var versionMismatch = cellEditorVersion !== this.cellEditorVersion;
if (versionMismatch || !this.editingCell) {
if (cellEditor.destroy) {
cellEditor.destroy();
}
return;
}
if (cellEditor.isCancelBeforeStart && cellEditor.isCancelBeforeStart()) {
if (cellEditor.destroy) {
cellEditor.destroy();
}
this.editingCell = false;
return;
}
if (!cellEditor.getGui) {
console.warn("ag-Grid: cellEditor for column " + this.column.getId() + " is missing getGui() method");
// no getGui, for React guys, see if they attached a react component directly
if (cellEditor.render) {
console.warn("ag-Grid: we found 'render' on the component, are you trying to set a React renderer but added it as colDef.cellEditor instead of colDef.cellEditorFmk?");
}
if (cellEditor.destroy) {
cellEditor.destroy();
}
this.editingCell = false;
return;
}
this.cellEditor = cellEditor;
this.cellEditorInPopup = cellEditor.isPopup && cellEditor.isPopup();
this.setInlineEditingClass();
if (this.cellEditorInPopup) {
this.addPopupCellEditor();
}
else {
this.addInCellEditor();
}
if (cellEditor.afterGuiAttached) {
cellEditor.afterGuiAttached();
}
var event = this.createEvent(null, events_1.Events.EVENT_CELL_EDITING_STARTED);
this.beans.eventService.dispatchEvent(event);
};
CellComp.prototype.addInCellEditor = function () {
utils_1._.removeAllChildren(this.getGui());
this.getGui().appendChild(this.cellEditor.getGui());
this.angular1Compile();
};
CellComp.prototype.addPopupCellEditor = function () {
var _this = this;
var ePopupGui = this.cellEditor.getGui();
this.hideEditorPopup = this.beans.popupService.addAsModalPopup(ePopupGui, true,
// callback for when popup disappears
function () {
_this.onPopupEditorClosed();
});
this.beans.popupService.positionPopupOverComponent({
column: this.column,
rowNode: this.rowNode,
type: 'popupCellEditor',
eventSource: this.getGui(),
ePopup: ePopupGui,
keepWithinBounds: true
});
this.angular1Compile();
};
CellComp.prototype.onPopupEditorClosed = function () {
// we only call stopEditing if we are editing, as
// it's possible the popup called 'stop editing'
// before this, eg if 'enter key' was pressed on
// the editor.
if (this.editingCell) {
// note: this only happens when use clicks outside of the grid. if use clicks on another
// cell, then the editing will have already stopped on this cell
this.stopRowOrCellEdit();
// we only focus cell again if this cell is still focused. it is possible
// it is not focused if the user cancelled the edit by clicking on another
// cell outside of this one
if (this.beans.focusedCellController.isCellFocused(this.gridCell)) {
this.focusCell(true);
}
}
};
// if we are editing inline, then we don't have the padding in the cell (set in the themes)
// to allow the text editor full access to the entire cell
CellComp.prototype.setInlineEditingClass = function () {
// ag-cell-inline-editing - appears when user is inline editing
// ag-cell-not-inline-editing - appears when user is no inline editing
// ag-cell-popup-editing - appears when user is editing cell in popup (appears on the cell, not on the popup)
// note: one of {ag-cell-inline-editing, ag-cell-not-inline-editing} is always present, they toggle.
// however {ag-cell-popup-editing} shows when popup, so you have both {ag-cell-popup-editing}
// and {ag-cell-not-inline-editing} showing at the same time.
var editingInline = this.editingCell && !this.cellEditorInPopup;
var popupEditorShowing = this.editingCell && this.cellEditorInPopup;
utils_1._.addOrRemoveCssClass(this.getGui(), "ag-cell-inline-editing", editingInline);
utils_1._.addOrRemoveCssClass(this.getGui(), "ag-cell-not-inline-editing", !editingInline);
utils_1._.addOrRemoveCssClass(this.getGui(), "ag-cell-popup-editing", popupEditorShowing);
utils_1._.addOrRemoveCssClass(this.getGui().parentNode, "ag-row-inline-editing", editingInline);
utils_1._.addOrRemoveCssClass(this.getGui().parentNode, "ag-row-not-inline-editing", !editingInline);
};
CellComp.prototype.createCellEditorParams = function (keyPress, charPress, cellStartedEdit) {
var params = {
value: this.getValue(),
keyPress: keyPress,
charPress: charPress,
column: this.column,
rowIndex: this.gridCell.rowIndex,
node: this.rowNode,
api: this.beans.gridOptionsWrapper.getApi(),
cellStartedEdit: cellStartedEdit,
columnApi: this.beans.gridOptionsWrapper.getColumnApi(),
context: this.beans.gridOptionsWrapper.getContext(),
$scope: this.scope,
onKeyDown: this.onKeyDown.bind(this),
stopEditing: this.stopEditingAndFocus.bind(this),
eGridCell: this.getGui(),
parseValue: this.parseValue.bind(this),
formatValue: this.formatValue.bind(this)
};
return params;
};
// cell editors call this, when they want to stop for reasons other
// than what we pick up on. eg selecting from a dropdown ends editing.
CellComp.prototype.stopEditingAndFocus = function (suppressNavigateAfterEdit) {
if (suppressNavigateAfterEdit === void 0) { suppressNavigateAfterEdit = false; }
this.stopRowOrCellEdit();
this.focusCell(true);
if (!suppressNavigateAfterEdit) {
this.navigateAfterEdit();
}
};
CellComp.prototype.parseValue = function (newValue) {
var params = {
node: this.rowNode,
data: this.rowNode.data,
oldValue: this.value,
newValue: newValue,
colDef: this.column.getColDef(),
column: this.column,
api: this.beans.gridOptionsWrapper.getApi(),
columnApi: this.beans.gridOptionsWrapper.getColumnApi(),
context: this.beans.gridOptionsWrapper.getContext()
};
var valueParser = this.column.getColDef().valueParser;
return utils_1._.exists(valueParser) ? this.beans.expressionService.evaluate(valueParser, params) : newValue;
};
CellComp.prototype.focusCell = function (forceBrowserFocus) {
if (forceBrowserFocus === void 0) { forceBrowserFocus = false; }
this.beans.focusedCellController.setFocusedCell(this.gridCell.rowIndex, this.column, this.rowNode.rowPinned, forceBrowserFocus);
};
CellComp.prototype.setFocusInOnEditor = function () {
if (this.editingCell) {
if (this.cellEditor && this.cellEditor.focusIn) {
// if the editor is present, then we just focus it
this.cellEditor.focusIn();
}
else {
// if the editor is not present, it means async cell editor (eg React fibre)
// and we are trying to set focus before the cell editor is present, so we
// focus the cell instead
this.focusCell(true);
}
}
};
CellComp.prototype.isEditing = function () {
return this.editingCell;
};
CellComp.prototype.onKeyDown = function (event) {
var key = event.which || event.keyCode;
// give user a chance to cancel event processing
if (this.doesUserWantToCancelKeyboardEvent(event)) {
return;
}
switch (key) {
case constants_1.Constants.KEY_ENTER:
this.onEnterKeyDown();
break;
case constants_1.Constants.KEY_F2:
this.onF2KeyDown();
break;
case constants_1.Constants.KEY_ESCAPE:
this.onEscapeKeyDown();
break;
case constants_1.Constants.KEY_TAB:
this.onTabKeyDown(event);
break;
case constants_1.Constants.KEY_BACKSPACE:
case constants_1.Constants.KEY_DELETE:
this.onBackspaceOrDeleteKeyPressed(key);
break;
case constants_1.Constants.KEY_DOWN:
case constants_1.Constants.KEY_UP:
case constants_1.Constants.KEY_RIGHT:
case constants_1.Constants.KEY_LEFT:
this.onNavigationKeyPressed(event, key);
break;
}
};
CellComp.prototype.doesUserWantToCancelKeyboardEvent = function (event) {
var callback = this.column.getColDef().suppressKeyboardEvent;
if (utils_1._.missing(callback)) {
return false;
}
else {
// if editing is null or undefined, this sets it to false
var params = {
event: event,
editing: this.editingCell,
column: this.column,
api: this.beans.gridOptionsWrapper.getApi(),
node: this.rowNode,
data: this.rowNode.data,
colDef: this.column.getColDef(),
context: this.beans.gridOptionsWrapper.getContext(),
columnApi: this.beans.gridOptionsWrapper.getColumnApi()
};
return callback(params);
}
};
CellComp.prototype.setFocusOutOnEditor = function () {
if (this.editingCell && this.cellEditor && this.cellEditor.focusOut) {
this.cellEditor.focusOut();
}
};
CellComp.prototype.onNaviga