UNPKG

70.8 kBJavaScriptView Raw
1/**
2 * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
3 * @version v18.1.2
4 * @link http://www.ag-grid.com/
5 * @license MIT
6 */
7"use strict";
8var __extends = (this && this.__extends) || (function () {
9 var extendStatics = Object.setPrototypeOf ||
10 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
11 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
12 return function (d, b) {
13 extendStatics(d, b);
14 function __() { this.constructor = d; }
15 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
16 };
17})();
18Object.defineProperty(exports, "__esModule", { value: true });
19var utils_1 = require("../utils");
20var column_1 = require("../entities/column");
21var rowNode_1 = require("../entities/rowNode");
22var constants_1 = require("../constants");
23var events_1 = require("../events");
24var gridCell_1 = require("../entities/gridCell");
25var component_1 = require("../widgets/component");
26var checkboxSelectionComponent_1 = require("./checkboxSelectionComponent");
27var rowDragComp_1 = require("./rowDragComp");
28var CellComp = (function (_super) {
29 __extends(CellComp, _super);
30 function CellComp(scope, beans, column, rowNode, rowComp, autoHeightCell) {
31 var _this = _super.call(this) || this;
32 _this.editingCell = false;
33 // every time we go into edit mode, or back again, this gets incremented.
34 // it's the components way of dealing with the async nature of framework components,
35 // so if a framework component takes a while to be created, we know if the object
36 // is still relevant when creating is finished. eg we could click edit / unedit 20
37 // times before the first React edit component comes back - we should discard
38 // the first 19.
39 _this.cellEditorVersion = 0;
40 _this.cellRendererVersion = 0;
41 _this.scope = scope;
42 _this.beans = beans;
43 _this.column = column;
44 _this.rowNode = rowNode;
45 _this.rowComp = rowComp;
46 _this.autoHeightCell = autoHeightCell;
47 _this.createGridCellVo();
48 _this.rangeSelectionEnabled = beans.enterprise && beans.gridOptionsWrapper.isEnableRangeSelection();
49 _this.cellFocused = _this.beans.focusedCellController.isCellFocused(_this.gridCell);
50 _this.firstRightPinned = _this.column.isFirstRightPinned();
51 _this.lastLeftPinned = _this.column.isLastLeftPinned();
52 if (_this.rangeSelectionEnabled) {
53 _this.rangeCount = _this.beans.rangeController.getCellRangeCount(_this.gridCell);
54 }
55 _this.getValueAndFormat();
56 _this.setUsingWrapper();
57 _this.chooseCellRenderer();
58 _this.setupColSpan();
59 _this.rowSpan = _this.column.getRowSpan(_this.rowNode);
60 return _this;
61 }
62 CellComp.prototype.getCreateTemplate = function () {
63 var templateParts = [];
64 var col = this.column;
65 var width = this.getCellWidth();
66 var left = col.getLeft();
67 var valueToRender = this.getInitialValueToRender();
68 var valueSanitised = utils_1._.get(this.column, 'colDef.template', null) ? valueToRender : utils_1._.escape(valueToRender);
69 this.tooltip = this.getToolTip();
70 var tooltipSanitised = utils_1._.escape(this.tooltip);
71 var colIdSanitised = utils_1._.escape(col.getId());
72 var wrapperStartTemplate;
73 var wrapperEndTemplate;
74 var stylesFromColDef = this.preProcessStylesFromColDef();
75 var cssClasses = this.getInitialCssClasses();
76 var stylesForRowSpanning = this.getStylesForRowSpanning();
77 if (this.usingWrapper) {
78 wrapperStartTemplate = '<span ref="eCellWrapper" class="ag-cell-wrapper"><span ref="eCellValue" class="ag-cell-value">';
79 wrapperEndTemplate = '</span></span>';
80 }
81 // hey, this looks like React!!!
82 templateParts.push("<div");
83 templateParts.push(" tabindex=\"-1\"");
84 templateParts.push(" role=\"gridcell\"");
85 templateParts.push(" comp-id=\"" + this.getCompId() + "\" ");
86 templateParts.push(" col-id=\"" + colIdSanitised + "\"");
87 templateParts.push(" class=\"" + cssClasses.join(' ') + "\"");
88 templateParts.push(tooltipSanitised ? " title=\"" + tooltipSanitised + "\"" : "");
89 templateParts.push(" style=\"width: " + width + "px; left: " + left + "px; " + stylesFromColDef + " " + stylesForRowSpanning + "\" >");
90 templateParts.push(wrapperStartTemplate);
91 templateParts.push(valueSanitised);
92 templateParts.push(wrapperEndTemplate);
93 templateParts.push("</div>");
94 return templateParts.join('');
95 };
96 CellComp.prototype.getStylesForRowSpanning = function () {
97 if (this.rowSpan === 1) {
98 return '';
99 }
100 var singleRowHeight = this.beans.gridOptionsWrapper.getRowHeightAsNumber();
101 var totalRowHeight = singleRowHeight * this.rowSpan;
102 return "height: " + totalRowHeight + "px; z-index: 1;";
103 };
104 CellComp.prototype.afterAttached = function () {
105 var querySelector = "[comp-id=\"" + this.getCompId() + "\"]";
106 var eGui = this.eParentRow.querySelector(querySelector);
107 this.setGui(eGui);
108 // all of these have dependencies on the eGui, so only do them after eGui is set
109 this.addDomData();
110 this.populateTemplate();
111 this.attachCellRenderer();
112 this.angular1Compile();
113 this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_CELL_FOCUSED, this.onCellFocused.bind(this));
114 this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_FLASH_CELLS, this.onFlashCells.bind(this));
115 this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_COLUMN_HOVER_CHANGED, this.onColumnHover.bind(this));
116 this.addDestroyableEventListener(this.rowNode, rowNode_1.RowNode.EVENT_ROW_INDEX_CHANGED, this.onRowIndexChanged.bind(this));
117 this.addDestroyableEventListener(this.rowNode, rowNode_1.RowNode.EVENT_CELL_CHANGED, this.onCellChanged.bind(this));
118 this.addDestroyableEventListener(this.column, column_1.Column.EVENT_LEFT_CHANGED, this.onLeftChanged.bind(this));
119 this.addDestroyableEventListener(this.column, column_1.Column.EVENT_WIDTH_CHANGED, this.onWidthChanged.bind(this));
120 this.addDestroyableEventListener(this.column, column_1.Column.EVENT_FIRST_RIGHT_PINNED_CHANGED, this.onFirstRightPinnedChanged.bind(this));
121 this.addDestroyableEventListener(this.column, column_1.Column.EVENT_LAST_LEFT_PINNED_CHANGED, this.onLastLeftPinnedChanged.bind(this));
122 // if not doing enterprise, then range selection service would be missing
123 // so need to check before trying to use it
124 if (this.rangeSelectionEnabled) {
125 this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_RANGE_SELECTION_CHANGED, this.onRangeSelectionChanged.bind(this));
126 }
127 };
128 CellComp.prototype.onColumnHover = function () {
129 var isHovered = this.beans.columnHoverService.isHovered(this.column);
130 utils_1._.addOrRemoveCssClass(this.getGui(), 'ag-column-hover', isHovered);
131 };
132 CellComp.prototype.onCellChanged = function (event) {
133 var eventImpactsThisCell = event.column === this.column;
134 if (eventImpactsThisCell) {
135 this.refreshCell({});
136 }
137 };
138 CellComp.prototype.getCellLeft = function () {
139 var mostLeftCol;
140 if (this.beans.gridOptionsWrapper.isEnableRtl() && this.colsSpanning) {
141 mostLeftCol = this.colsSpanning[this.colsSpanning.length - 1];
142 }
143 else {
144 mostLeftCol = this.column;
145 }
146 return mostLeftCol.getLeft();
147 };
148 CellComp.prototype.getCellWidth = function () {
149 if (this.colsSpanning) {
150 var result_1 = 0;
151 this.colsSpanning.forEach(function (col) { return result_1 += col.getActualWidth(); });
152 return result_1;
153 }
154 else {
155 return this.column.getActualWidth();
156 }
157 };
158 CellComp.prototype.onFlashCells = function (event) {
159 var cellId = this.gridCell.createId();
160 var shouldFlash = event.cells[cellId];
161 if (shouldFlash) {
162 this.animateCell('highlight');
163 }
164 };
165 CellComp.prototype.setupColSpan = function () {
166 // if no col span is active, then we don't set it up, as it would be wasteful of CPU
167 if (utils_1._.missing(this.column.getColDef().colSpan)) {
168 return;
169 }
170 // because we are col spanning, a reorder of the cols can change what cols we are spanning over
171 this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_DISPLAYED_COLUMNS_CHANGED, this.onDisplayColumnsChanged.bind(this));
172 // because we are spanning over multiple cols, we check for width any time any cols width changes.
173 // this is expensive - really we should be explicitly checking only the cols we are spanning over
174 // instead of every col, however it would be tricky code to track the cols we are spanning over, so
175 // because hardly anyone will be using colSpan, am favoring this easier way for more maintainable code.
176 this.addDestroyableEventListener(this.beans.eventService, events_1.Events.EVENT_DISPLAYED_COLUMNS_WIDTH_CHANGED, this.onWidthChanged.bind(this));
177 this.colsSpanning = this.getColSpanningList();
178 };
179 CellComp.prototype.getColSpanningList = function () {
180 var colSpan = this.column.getColSpan(this.rowNode);
181 var colsSpanning = [];
182 // if just one col, the col span is just the column we are in
183 if (colSpan === 1) {
184 colsSpanning.push(this.column);
185 }
186 else {
187 var pointer = this.column;
188 var pinned = this.column.getPinned();
189 for (var i = 0; i < colSpan; i++) {
190 colsSpanning.push(pointer);
191 pointer = this.beans.columnController.getDisplayedColAfter(pointer);
192 if (utils_1._.missing(pointer)) {
193 break;
194 }
195 // we do not allow col spanning to span outside of pinned areas
196 if (pinned !== pointer.getPinned()) {
197 break;
198 }
199 }
200 }
201 return colsSpanning;
202 };
203 CellComp.prototype.onDisplayColumnsChanged = function () {
204 var colsSpanning = this.getColSpanningList();
205 if (!utils_1._.compareArrays(this.colsSpanning, colsSpanning)) {
206 this.colsSpanning = colsSpanning;
207 this.onWidthChanged();
208 this.onLeftChanged(); // left changes when doing RTL
209 }
210 };
211 CellComp.prototype.getInitialCssClasses = function () {
212 var cssClasses = ["ag-cell", "ag-cell-not-inline-editing"];
213 // if we are putting the cell into a dummy container, to work out it's height,
214 // then we don't put the height css in, as we want cell to fit height in that case.
215 if (!this.autoHeightCell) {
216 cssClasses.push('ag-cell-with-height');
217 }
218 var doingFocusCss = !this.beans.gridOptionsWrapper.isSuppressCellSelection();
219 if (doingFocusCss) {
220 // otherwise the class depends on the focus state
221 cssClasses.push(this.cellFocused ? 'ag-cell-focus' : 'ag-cell-no-focus');
222 }
223 else {
224 // if we are not doing cell selection, then ag-cell-no-focus gets put onto every cell
225 cssClasses.push('ag-cell-no-focus');
226 }
227 if (this.firstRightPinned) {
228 cssClasses.push('ag-cell-first-right-pinned');
229 }
230 if (this.lastLeftPinned) {
231 cssClasses.push('ag-cell-last-left-pinned');
232 }
233 if (this.beans.columnHoverService.isHovered(this.column)) {
234 cssClasses.push('ag-column-hover');
235 }
236 utils_1._.pushAll(cssClasses, this.preProcessClassesFromColDef());
237 utils_1._.pushAll(cssClasses, this.preProcessCellClassRules());
238 utils_1._.pushAll(cssClasses, this.getRangeClasses());
239 // if using the wrapper, this class goes on the wrapper instead
240 if (!this.usingWrapper) {
241 cssClasses.push('ag-cell-value');
242 }
243 return cssClasses;
244 };
245 CellComp.prototype.getInitialValueToRender = function () {
246 // if using a cellRenderer, then render the html from the cell renderer if it exists
247 if (this.usingCellRenderer) {
248 if (typeof this.cellRendererGui === 'string') {
249 return this.cellRendererGui;
250 }
251 else {
252 return '';
253 }
254 }
255 var colDef = this.column.getColDef();
256 if (colDef.template) {
257 // template is really only used for angular 1 - as people using ng1 are used to providing templates with
258 // bindings in it. in ng2, people will hopefully want to provide components, not templates.
259 return colDef.template;
260 }
261 else if (colDef.templateUrl) {
262 // likewise for templateUrl - it's for ng1 really - when we move away from ng1, we can take these out.
263 // niall was pro angular 1 when writing template and templateUrl, if writing from scratch now, would
264 // not do these, but would follow a pattern that was friendly towards components, not templates.
265 var template = this.beans.templateService.getTemplate(colDef.templateUrl, this.refreshCell.bind(this, true));
266 if (template) {
267 return template;
268 }
269 else {
270 return '';
271 }
272 }
273 else {
274 return this.getValueToUse();
275 }
276 };
277 CellComp.prototype.getRenderedRow = function () {
278 return this.rowComp;
279 };
280 CellComp.prototype.isSuppressNavigable = function () {
281 return this.column.isSuppressNavigable(this.rowNode);
282 };
283 CellComp.prototype.getCellRenderer = function () {
284 return this.cellRenderer;
285 };
286 CellComp.prototype.getCellEditor = function () {
287 return this.cellEditor;
288 };
289 // + stop editing {forceRefresh: true, suppressFlash: true}
290 // + event cellChanged {}
291 // + cellRenderer.params.refresh() {} -> method passes 'as is' to the cellRenderer, so params could be anything
292 // + rowComp: event dataChanged {animate: update, newData: !update}
293 // + rowComp: api refreshCells() {animate: true/false}
294 // + rowRenderer: api softRefreshView() {}
295 CellComp.prototype.refreshCell = function (params) {
296 if (this.editingCell) {
297 return;
298 }
299 var newData = params && params.newData;
300 var suppressFlash = (params && params.suppressFlash) || this.column.getColDef().suppressCellFlash;
301 var forceRefresh = params && params.forceRefresh;
302 var oldValue = this.value;
303 this.getValueAndFormat();
304 // for simple values only (not pojo's), see if the value is the same, and if it is, skip the refresh.
305 // when never allow skipping after an edit, as after editing, we need to put the GUI back to the way
306 // if was before the edit.
307 var valuesDifferent = !this.valuesAreEqual(oldValue, this.value);
308 var dataNeedsUpdating = forceRefresh || valuesDifferent;
309 if (dataNeedsUpdating) {
310 var cellRendererRefreshed = void 0;
311 // if it's 'new data', then we don't refresh the cellRenderer, even if refresh method is available.
312 // this is because if the whole data is new (ie we are showing stock price 'BBA' now and not 'SSD')
313 // then we are not showing a movement in the stock price, rather we are showing different stock.
314 if (newData || suppressFlash) {
315 cellRendererRefreshed = false;
316 }
317 else {
318 cellRendererRefreshed = this.attemptCellRendererRefresh();
319 }
320 // we do the replace if not doing refresh, or if refresh was unsuccessful.
321 // the refresh can be unsuccessful if we are using a framework (eg ng2 or react) and the framework
322 // wrapper has the refresh method, but the underlying component doesn't
323 if (!cellRendererRefreshed) {
324 this.replaceContentsAfterRefresh();
325 }
326 if (!suppressFlash) {
327 var flashCell = this.beans.gridOptionsWrapper.isEnableCellChangeFlash()
328 || this.column.getColDef().enableCellChangeFlash;
329 if (flashCell) {
330 this.flashCell();
331 }
332 }
333 // need to check rules. note, we ignore colDef classes and styles, these are assumed to be static
334 this.postProcessStylesFromColDef();
335 this.postProcessClassesFromColDef();
336 }
337 this.refreshToolTip();
338 // we do cellClassRules even if the value has not changed, so that users who have rules that
339 // look at other parts of the row (where the other part of the row might of changed) will work.
340 this.postProcessCellClassRules();
341 };
342 // user can also call this via API
343 CellComp.prototype.flashCell = function () {
344 this.animateCell('data-changed');
345 };
346 CellComp.prototype.animateCell = function (cssName) {
347 var fullName = 'ag-cell-' + cssName;
348 var animationFullName = 'ag-cell-' + cssName + '-animation';
349 var element = this.getGui();
350 // we want to highlight the cells, without any animation
351 utils_1._.addCssClass(element, fullName);
352 utils_1._.removeCssClass(element, animationFullName);
353 // then once that is applied, we remove the highlight with animation
354 setTimeout(function () {
355 utils_1._.removeCssClass(element, fullName);
356 utils_1._.addCssClass(element, animationFullName);
357 setTimeout(function () {
358 // and then to leave things as we got them, we remove the animation
359 utils_1._.removeCssClass(element, animationFullName);
360 }, 1000);
361 }, 500);
362 };
363 CellComp.prototype.replaceContentsAfterRefresh = function () {
364 // otherwise we rip out the cell and replace it
365 utils_1._.removeAllChildren(this.eParentOfValue);
366 // remove old renderer component if it exists
367 if (this.cellRenderer && this.cellRenderer.destroy) {
368 this.cellRenderer.destroy();
369 }
370 this.cellRenderer = null;
371 this.cellRendererGui = null;
372 // populate
373 this.putDataIntoCellAfterRefresh();
374 this.angular1Compile();
375 };
376 CellComp.prototype.angular1Compile = function () {
377 // if angular compiling, then need to also compile the cell again (angular compiling sucks, please wait...)
378 if (this.beans.gridOptionsWrapper.isAngularCompileRows()) {
379 var eGui = this.getGui();
380 var compiledElement_1 = this.beans.$compile(eGui)(this.scope);
381 this.addDestroyFunc(function () {
382 compiledElement_1.remove();
383 });
384 }
385 };
386 CellComp.prototype.postProcessStylesFromColDef = function () {
387 var stylesToUse = this.processStylesFromColDef();
388 if (stylesToUse) {
389 utils_1._.addStylesToElement(this.getGui(), stylesToUse);
390 }
391 };
392 CellComp.prototype.preProcessStylesFromColDef = function () {
393 var stylesToUse = this.processStylesFromColDef();
394 return utils_1._.cssStyleObjectToMarkup(stylesToUse);
395 };
396 CellComp.prototype.processStylesFromColDef = function () {
397 var colDef = this.column.getColDef();
398 if (colDef.cellStyle) {
399 var cssToUse = void 0;
400 if (typeof colDef.cellStyle === 'function') {
401 var cellStyleParams = {
402 value: this.value,
403 data: this.rowNode.data,
404 node: this.rowNode,
405 colDef: colDef,
406 column: this.column,
407 $scope: this.scope,
408 context: this.beans.gridOptionsWrapper.getContext(),
409 api: this.beans.gridOptionsWrapper.getApi()
410 };
411 var cellStyleFunc = colDef.cellStyle;
412 cssToUse = cellStyleFunc(cellStyleParams);
413 }
414 else {
415 cssToUse = colDef.cellStyle;
416 }
417 return cssToUse;
418 }
419 };
420 CellComp.prototype.postProcessClassesFromColDef = function () {
421 var _this = this;
422 this.processClassesFromColDef(function (className) { return utils_1._.addCssClass(_this.getGui(), className); });
423 };
424 CellComp.prototype.preProcessClassesFromColDef = function () {
425 var res = [];
426 this.processClassesFromColDef(function (className) { return res.push(className); });
427 return res;
428 };
429 CellComp.prototype.processClassesFromColDef = function (onApplicableClass) {
430 this.beans.stylingService.processStaticCellClasses(this.column.getColDef(), {
431 value: this.value,
432 data: this.rowNode.data,
433 node: this.rowNode,
434 colDef: this.column.getColDef(),
435 rowIndex: this.rowNode.rowIndex,
436 $scope: this.scope,
437 api: this.beans.gridOptionsWrapper.getApi(),
438 context: this.beans.gridOptionsWrapper.getContext()
439 }, onApplicableClass);
440 };
441 CellComp.prototype.putDataIntoCellAfterRefresh = function () {
442 // template gets preference, then cellRenderer, then do it ourselves
443 var colDef = this.column.getColDef();
444 if (colDef.template) {
445 // template is really only used for angular 1 - as people using ng1 are used to providing templates with
446 // bindings in it. in ng2, people will hopefully want to provide components, not templates.
447 this.eParentOfValue.innerHTML = colDef.template;
448 }
449 else if (colDef.templateUrl) {
450 // likewise for templateUrl - it's for ng1 really - when we move away from ng1, we can take these out.
451 // niall was pro angular 1 when writing template and templateUrl, if writing from scratch now, would
452 // not do these, but would follow a pattern that was friendly towards components, not templates.
453 var template = this.beans.templateService.getTemplate(colDef.templateUrl, this.refreshCell.bind(this, true));
454 if (template) {
455 this.eParentOfValue.innerHTML = template;
456 }
457 // use cell renderer if it exists
458 }
459 else if (this.usingCellRenderer) {
460 this.attachCellRenderer();
461 }
462 else {
463 var valueToUse = this.getValueToUse();
464 if (valueToUse !== null && valueToUse !== undefined) {
465 this.eParentOfValue.innerText = valueToUse;
466 }
467 }
468 };
469 CellComp.prototype.attemptCellRendererRefresh = function () {
470 if (utils_1._.missing(this.cellRenderer) || utils_1._.missing(this.cellRenderer.refresh)) {
471 return false;
472 }
473 // if the cell renderer has a refresh method, we call this instead of doing a refresh
474 // note: should pass in params here instead of value?? so that client has formattedValue
475 var params = this.createCellRendererParams();
476 var result = this.cellRenderer.refresh(params);
477 // NOTE on undefined: previous version of the cellRenderer.refresh() interface
478 // returned nothing, if the method existed, we assumed it refreshed. so for
479 // backwards compatibility, we assume if method exists and returns nothing,
480 // that it was successful.
481 return result === true || result === undefined;
482 };
483 CellComp.prototype.refreshToolTip = function () {
484 var newTooltip = this.getToolTip();
485 if (this.tooltip !== newTooltip) {
486 this.tooltip = newTooltip;
487 if (utils_1._.exists(newTooltip)) {
488 var tooltipSanitised = utils_1._.escape(this.tooltip);
489 this.eParentOfValue.setAttribute('title', tooltipSanitised);
490 }
491 else {
492 this.eParentOfValue.removeAttribute('title');
493 }
494 }
495 };
496 CellComp.prototype.valuesAreEqual = function (val1, val2) {
497 // if the user provided an equals method, use that, otherwise do simple comparison
498 var colDef = this.column.getColDef();
499 var equalsMethod = colDef ? colDef.equals : null;
500 if (equalsMethod) {
501 return equalsMethod(val1, val2);
502 }
503 else {
504 return val1 === val2;
505 }
506 };
507 CellComp.prototype.getToolTip = function () {
508 var colDef = this.column.getColDef();
509 var data = this.rowNode.data;
510 if (colDef.tooltipField && utils_1._.exists(data)) {
511 return utils_1._.getValueUsingField(data, colDef.tooltipField, this.column.isTooltipFieldContainsDots());
512 }
513 else if (colDef.tooltip) {
514 return colDef.tooltip({
515 value: this.value,
516 valueFormatted: this.valueFormatted,
517 data: this.rowNode.data,
518 node: this.rowNode,
519 colDef: this.column.getColDef(),
520 api: this.beans.gridOptionsWrapper.getApi(),
521 $scope: this.scope,
522 context: this.beans.gridOptionsWrapper.getContext(),
523 rowIndex: this.gridCell.rowIndex
524 });
525 }
526 else {
527 return null;
528 }
529 };
530 CellComp.prototype.processCellClassRules = function (onApplicableClass, onNotApplicableClass) {
531 this.beans.stylingService.processClassRules(this.column.getColDef().cellClassRules, {
532 value: this.value,
533 data: this.rowNode.data,
534 node: this.rowNode,
535 colDef: this.column.getColDef(),
536 rowIndex: this.gridCell.rowIndex,
537 api: this.beans.gridOptionsWrapper.getApi(),
538 $scope: this.scope,
539 context: this.beans.gridOptionsWrapper.getContext()
540 }, onApplicableClass, onNotApplicableClass);
541 };
542 CellComp.prototype.postProcessCellClassRules = function () {
543 var _this = this;
544 this.processCellClassRules(function (className) {
545 utils_1._.addCssClass(_this.getGui(), className);
546 }, function (className) {
547 utils_1._.removeCssClass(_this.getGui(), className);
548 });
549 };
550 CellComp.prototype.preProcessCellClassRules = function () {
551 var res = [];
552 this.processCellClassRules(function (className) {
553 res.push(className);
554 }, function (className) {
555 // not catered for, if creating, no need
556 // to remove class as it was never there
557 });
558 return res;
559 };
560 // a wrapper is used when we are putting a selection checkbox in the cell with the value
561 CellComp.prototype.setUsingWrapper = function () {
562 var colDef = this.column.getColDef();
563 // never allow selection or dragging on pinned rows
564 if (this.rowNode.rowPinned) {
565 this.usingWrapper = false;
566 this.includeSelectionComponent = false;
567 this.includeRowDraggingComponent = false;
568 return;
569 }
570 var cbSelectionIsFunc = typeof colDef.checkboxSelection === 'function';
571 var rowDraggableIsFunc = typeof colDef.rowDrag === 'function';
572 this.includeSelectionComponent = cbSelectionIsFunc || colDef.checkboxSelection === true;
573 this.includeRowDraggingComponent = rowDraggableIsFunc || colDef.rowDrag === true;
574 this.usingWrapper = this.includeRowDraggingComponent || this.includeSelectionComponent;
575 };
576 CellComp.prototype.chooseCellRenderer = function () {
577 // template gets preference, then cellRenderer, then do it ourselves
578 var colDef = this.column.getColDef();
579 // templates are for ng1, ideally we wouldn't have these, they are ng1 support
580 // inside the core which is bad
581 if (colDef.template || colDef.templateUrl) {
582 this.usingCellRenderer = false;
583 return;
584 }
585 var params = this.createCellRendererParams();
586 var cellRenderer = this.beans.componentResolver.getComponentToUse(colDef, 'cellRenderer', params, null);
587 var pinnedRowCellRenderer = this.beans.componentResolver.getComponentToUse(colDef, 'pinnedRowCellRenderer', params, null);
588 if (pinnedRowCellRenderer && this.rowNode.rowPinned) {
589 this.cellRendererType = 'pinnedRowCellRenderer';
590 this.usingCellRenderer = true;
591 }
592 else if (cellRenderer) {
593 this.cellRendererType = 'cellRenderer';
594 this.usingCellRenderer = true;
595 }
596 else {
597 this.usingCellRenderer = false;
598 }
599 };
600 CellComp.prototype.createCellRendererInstance = function () {
601 var params = this.createCellRendererParams();
602 this.cellRendererVersion++;
603 var callback = this.afterCellRendererCreated.bind(this, this.cellRendererVersion);
604 this.beans.componentResolver.createAgGridComponent(this.column.getColDef(), params, this.cellRendererType, params, null).then(callback);
605 };
606 CellComp.prototype.afterCellRendererCreated = function (cellRendererVersion, cellRenderer) {
607 // see if daemon
608 if (!this.isAlive() || (cellRendererVersion !== this.cellRendererVersion)) {
609 if (cellRenderer.destroy) {
610 cellRenderer.destroy();
611 }
612 return;
613 }
614 this.cellRenderer = cellRenderer;
615 this.cellRendererGui = this.cellRenderer.getGui();
616 if (utils_1._.missing(this.cellRendererGui)) {
617 return;
618 }
619 // if async components, then it's possible the user started editing since
620 // this call was made
621 if (!this.editingCell) {
622 this.eParentOfValue.appendChild(this.cellRendererGui);
623 }
624 };
625 CellComp.prototype.attachCellRenderer = function () {
626 if (!this.usingCellRenderer) {
627 return;
628 }
629 this.createCellRendererInstance();
630 };
631 CellComp.prototype.createCellRendererParams = function () {
632 var _this = this;
633 var params = {
634 value: this.value,
635 valueFormatted: this.valueFormatted,
636 getValue: this.getValue.bind(this),
637 setValue: function (value) {
638 _this.beans.valueService.setValue(_this.rowNode, _this.column, value);
639 },
640 formatValue: this.formatValue.bind(this),
641 data: this.rowNode.data,
642 node: this.rowNode,
643 colDef: this.column.getColDef(),
644 column: this.column,
645 $scope: this.scope,
646 rowIndex: this.gridCell.rowIndex,
647 api: this.beans.gridOptionsWrapper.getApi(),
648 columnApi: this.beans.gridOptionsWrapper.getColumnApi(),
649 context: this.beans.gridOptionsWrapper.getContext(),
650 refreshCell: this.refreshCell.bind(this),
651 eGridCell: this.getGui(),
652 eParentOfValue: this.eParentOfValue,
653 // these bits are not documented anywhere, so we could drop them?
654 // it was in the olden days to allow user to register for when rendered
655 // row was removed (the row comp was removed), however now that the user
656 // can provide components for cells, the destroy method gets call when this
657 // happens so no longer need to fire event.
658 addRowCompListener: this.rowComp ? this.rowComp.addEventListener.bind(this.rowComp) : null,
659 addRenderedRowListener: function (eventType, listener) {
660 console.warn('ag-Grid: since ag-Grid .v11, params.addRenderedRowListener() is now params.addRowCompListener()');
661 if (_this.rowComp) {
662 _this.rowComp.addEventListener(eventType, listener);
663 }
664 }
665 };
666 return params;
667 };
668 CellComp.prototype.formatValue = function (value) {
669 var valueFormatted = this.beans.valueFormatterService.formatValue(this.column, this.rowNode, this.scope, value);
670 var valueFormattedExists = valueFormatted !== null && valueFormatted !== undefined;
671 return valueFormattedExists ? valueFormatted : value;
672 };
673 CellComp.prototype.getValueToUse = function () {
674 var valueFormattedExists = this.valueFormatted !== null && this.valueFormatted !== undefined;
675 return valueFormattedExists ? this.valueFormatted : this.value;
676 };
677 CellComp.prototype.getValueAndFormat = function () {
678 this.value = this.getValue();
679 this.valueFormatted = this.beans.valueFormatterService.formatValue(this.column, this.rowNode, this.scope, this.value);
680 };
681 CellComp.prototype.getValue = function () {
682 // if we don't check this, then the grid will render leaf groups as open even if we are not
683 // allowing the user to open leaf groups. confused? remember for pivot mode we don't allow
684 // opening leaf groups, so we have to force leafGroups to be closed in case the user expanded
685 // them via the API, or user user expanded them in the UI before turning on pivot mode
686 var lockedClosedGroup = this.rowNode.leafGroup && this.beans.columnController.isPivotMode();
687 var isOpenGroup = this.rowNode.group && this.rowNode.expanded && !this.rowNode.footer && !lockedClosedGroup;
688 if (isOpenGroup && this.beans.gridOptionsWrapper.isGroupIncludeFooter()) {
689 // if doing grouping and footers, we don't want to include the agg value
690 // in the header when the group is open
691 return this.beans.valueService.getValue(this.column, this.rowNode, false, true);
692 }
693 else {
694 return this.beans.valueService.getValue(this.column, this.rowNode);
695 }
696 };
697 CellComp.prototype.onMouseEvent = function (eventName, mouseEvent) {
698 if (utils_1._.isStopPropagationForAgGrid(mouseEvent)) {
699 return;
700 }
701 switch (eventName) {
702 case 'click':
703 this.onCellClicked(mouseEvent);
704 break;
705 case 'mousedown':
706 this.onMouseDown(mouseEvent);
707 break;
708 case 'dblclick':
709 this.onCellDoubleClicked(mouseEvent);
710 break;
711 case 'mouseout':
712 this.onMouseOut(mouseEvent);
713 break;
714 case 'mouseover':
715 this.onMouseOver(mouseEvent);
716 break;
717 }
718 };
719 CellComp.prototype.dispatchCellContextMenuEvent = function (event) {
720 var colDef = this.column.getColDef();
721 var cellContextMenuEvent = this.createEvent(event, events_1.Events.EVENT_CELL_CONTEXT_MENU);
722 this.beans.eventService.dispatchEvent(cellContextMenuEvent);
723 if (colDef.onCellContextMenu) {
724 // to make the callback async, do in a timeout
725 setTimeout(function () { return colDef.onCellContextMenu(cellContextMenuEvent); }, 0);
726 }
727 };
728 CellComp.prototype.createEvent = function (domEvent, eventType) {
729 var event = {
730 node: this.rowNode,
731 data: this.rowNode.data,
732 value: this.value,
733 column: this.column,
734 colDef: this.column.getColDef(),
735 context: this.beans.gridOptionsWrapper.getContext(),
736 api: this.beans.gridApi,
737 columnApi: this.beans.columnApi,
738 rowPinned: this.rowNode.rowPinned,
739 event: domEvent,
740 type: eventType,
741 rowIndex: this.rowNode.rowIndex
742 };
743 // because we are hacking in $scope for angular 1, we have to de-reference
744 if (this.scope) {
745 event.$scope = this.scope;
746 }
747 return event;
748 };
749 CellComp.prototype.onMouseOut = function (mouseEvent) {
750 var cellMouseOutEvent = this.createEvent(mouseEvent, events_1.Events.EVENT_CELL_MOUSE_OUT);
751 this.beans.eventService.dispatchEvent(cellMouseOutEvent);
752 this.beans.columnHoverService.clearMouseOver();
753 };
754 CellComp.prototype.onMouseOver = function (mouseEvent) {
755 var cellMouseOverEvent = this.createEvent(mouseEvent, events_1.Events.EVENT_CELL_MOUSE_OVER);
756 this.beans.eventService.dispatchEvent(cellMouseOverEvent);
757 this.beans.columnHoverService.setMouseOver([this.column]);
758 };
759 CellComp.prototype.onCellDoubleClicked = function (mouseEvent) {
760 var colDef = this.column.getColDef();
761 // always dispatch event to eventService
762 var cellDoubleClickedEvent = this.createEvent(mouseEvent, events_1.Events.EVENT_CELL_DOUBLE_CLICKED);
763 this.beans.eventService.dispatchEvent(cellDoubleClickedEvent);
764 // check if colDef also wants to handle event
765 if (typeof colDef.onCellDoubleClicked === 'function') {
766 // to make the callback async, do in a timeout
767 setTimeout(function () { return colDef.onCellDoubleClicked(cellDoubleClickedEvent); }, 0);
768 }
769 var editOnDoubleClick = !this.beans.gridOptionsWrapper.isSingleClickEdit()
770 && !this.beans.gridOptionsWrapper.isSuppressClickEdit();
771 if (editOnDoubleClick) {
772 this.startRowOrCellEdit();
773 }
774 };
775 // called by rowRenderer when user navigates via tab key
776 CellComp.prototype.startRowOrCellEdit = function (keyPress, charPress) {
777 if (this.beans.gridOptionsWrapper.isFullRowEdit()) {
778 this.rowComp.startRowEditing(keyPress, charPress, this);
779 }
780 else {
781 this.startEditingIfEnabled(keyPress, charPress, true);
782 }
783 };
784 CellComp.prototype.isCellEditable = function () {
785 return this.column.isCellEditable(this.rowNode);
786 };
787 // either called internally if single cell editing, or called by rowRenderer if row editing
788 CellComp.prototype.startEditingIfEnabled = function (keyPress, charPress, cellStartedEdit) {
789 if (keyPress === void 0) { keyPress = null; }
790 if (charPress === void 0) { charPress = null; }
791 if (cellStartedEdit === void 0) { cellStartedEdit = false; }
792 // don't do it if not editable
793 if (!this.isCellEditable()) {
794 return;
795 }
796 // don't do it if already editing
797 if (this.editingCell) {
798 return;
799 }
800 this.editingCell = true;
801 this.cellEditorVersion++;
802 var callback = this.afterCellEditorCreated.bind(this, this.cellEditorVersion);
803 var params = this.createCellEditorParams(keyPress, charPress, cellStartedEdit);
804 this.beans.cellEditorFactory.createCellEditor(this.column.getColDef(), params).then(callback);
805 // if we don't do this, and editor component is async, then there will be a period
806 // when the component isn't present and keyboard navigation won't work - so example
807 // of user hitting tab quickly (more quickly than renderers getting created) won't work
808 var cellEditorAsync = utils_1._.missing(this.cellEditor);
809 if (cellEditorAsync && cellStartedEdit) {
810 this.focusCell(true);
811 }
812 };
813 CellComp.prototype.afterCellEditorCreated = function (cellEditorVersion, cellEditor) {
814 // if editingCell=false, means user cancelled the editor before component was ready.
815 // if versionMismatch, then user cancelled the edit, then started the edit again, and this
816 // is the first editor which is now stale.
817 var versionMismatch = cellEditorVersion !== this.cellEditorVersion;
818 if (versionMismatch || !this.editingCell) {
819 if (cellEditor.destroy) {
820 cellEditor.destroy();
821 }
822 return;
823 }
824 if (cellEditor.isCancelBeforeStart && cellEditor.isCancelBeforeStart()) {
825 if (cellEditor.destroy) {
826 cellEditor.destroy();
827 }
828 this.editingCell = false;
829 return;
830 }
831 if (!cellEditor.getGui) {
832 console.warn("ag-Grid: cellEditor for column " + this.column.getId() + " is missing getGui() method");
833 // no getGui, for React guys, see if they attached a react component directly
834 if (cellEditor.render) {
835 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?");
836 }
837 if (cellEditor.destroy) {
838 cellEditor.destroy();
839 }
840 this.editingCell = false;
841 return;
842 }
843 this.cellEditor = cellEditor;
844 this.cellEditorInPopup = cellEditor.isPopup && cellEditor.isPopup();
845 this.setInlineEditingClass();
846 if (this.cellEditorInPopup) {
847 this.addPopupCellEditor();
848 }
849 else {
850 this.addInCellEditor();
851 }
852 if (cellEditor.afterGuiAttached) {
853 cellEditor.afterGuiAttached();
854 }
855 var event = this.createEvent(null, events_1.Events.EVENT_CELL_EDITING_STARTED);
856 this.beans.eventService.dispatchEvent(event);
857 };
858 CellComp.prototype.addInCellEditor = function () {
859 utils_1._.removeAllChildren(this.getGui());
860 this.getGui().appendChild(this.cellEditor.getGui());
861 this.angular1Compile();
862 };
863 CellComp.prototype.addPopupCellEditor = function () {
864 var _this = this;
865 var ePopupGui = this.cellEditor.getGui();
866 this.hideEditorPopup = this.beans.popupService.addAsModalPopup(ePopupGui, true,
867 // callback for when popup disappears
868 function () {
869 _this.onPopupEditorClosed();
870 });
871 this.beans.popupService.positionPopupOverComponent({
872 column: this.column,
873 rowNode: this.rowNode,
874 type: 'popupCellEditor',
875 eventSource: this.getGui(),
876 ePopup: ePopupGui,
877 keepWithinBounds: true
878 });
879 this.angular1Compile();
880 };
881 CellComp.prototype.onPopupEditorClosed = function () {
882 // we only call stopEditing if we are editing, as
883 // it's possible the popup called 'stop editing'
884 // before this, eg if 'enter key' was pressed on
885 // the editor.
886 if (this.editingCell) {
887 // note: this only happens when use clicks outside of the grid. if use clicks on another
888 // cell, then the editing will have already stopped on this cell
889 this.stopRowOrCellEdit();
890 // we only focus cell again if this cell is still focused. it is possible
891 // it is not focused if the user cancelled the edit by clicking on another
892 // cell outside of this one
893 if (this.beans.focusedCellController.isCellFocused(this.gridCell)) {
894 this.focusCell(true);
895 }
896 }
897 };
898 // if we are editing inline, then we don't have the padding in the cell (set in the themes)
899 // to allow the text editor full access to the entire cell
900 CellComp.prototype.setInlineEditingClass = function () {
901 // ag-cell-inline-editing - appears when user is inline editing
902 // ag-cell-not-inline-editing - appears when user is no inline editing
903 // ag-cell-popup-editing - appears when user is editing cell in popup (appears on the cell, not on the popup)
904 // note: one of {ag-cell-inline-editing, ag-cell-not-inline-editing} is always present, they toggle.
905 // however {ag-cell-popup-editing} shows when popup, so you have both {ag-cell-popup-editing}
906 // and {ag-cell-not-inline-editing} showing at the same time.
907 var editingInline = this.editingCell && !this.cellEditorInPopup;
908 var popupEditorShowing = this.editingCell && this.cellEditorInPopup;
909 utils_1._.addOrRemoveCssClass(this.getGui(), "ag-cell-inline-editing", editingInline);
910 utils_1._.addOrRemoveCssClass(this.getGui(), "ag-cell-not-inline-editing", !editingInline);
911 utils_1._.addOrRemoveCssClass(this.getGui(), "ag-cell-popup-editing", popupEditorShowing);
912 utils_1._.addOrRemoveCssClass(this.getGui().parentNode, "ag-row-inline-editing", editingInline);
913 utils_1._.addOrRemoveCssClass(this.getGui().parentNode, "ag-row-not-inline-editing", !editingInline);
914 };
915 CellComp.prototype.createCellEditorParams = function (keyPress, charPress, cellStartedEdit) {
916 var params = {
917 value: this.getValue(),
918 keyPress: keyPress,
919 charPress: charPress,
920 column: this.column,
921 rowIndex: this.gridCell.rowIndex,
922 node: this.rowNode,
923 api: this.beans.gridOptionsWrapper.getApi(),
924 cellStartedEdit: cellStartedEdit,
925 columnApi: this.beans.gridOptionsWrapper.getColumnApi(),
926 context: this.beans.gridOptionsWrapper.getContext(),
927 $scope: this.scope,
928 onKeyDown: this.onKeyDown.bind(this),
929 stopEditing: this.stopEditingAndFocus.bind(this),
930 eGridCell: this.getGui(),
931 parseValue: this.parseValue.bind(this),
932 formatValue: this.formatValue.bind(this)
933 };
934 return params;
935 };
936 // cell editors call this, when they want to stop for reasons other
937 // than what we pick up on. eg selecting from a dropdown ends editing.
938 CellComp.prototype.stopEditingAndFocus = function (suppressNavigateAfterEdit) {
939 if (suppressNavigateAfterEdit === void 0) { suppressNavigateAfterEdit = false; }
940 this.stopRowOrCellEdit();
941 this.focusCell(true);
942 if (!suppressNavigateAfterEdit) {
943 this.navigateAfterEdit();
944 }
945 };
946 CellComp.prototype.parseValue = function (newValue) {
947 var params = {
948 node: this.rowNode,
949 data: this.rowNode.data,
950 oldValue: this.value,
951 newValue: newValue,
952 colDef: this.column.getColDef(),
953 column: this.column,
954 api: this.beans.gridOptionsWrapper.getApi(),
955 columnApi: this.beans.gridOptionsWrapper.getColumnApi(),
956 context: this.beans.gridOptionsWrapper.getContext()
957 };
958 var valueParser = this.column.getColDef().valueParser;
959 return utils_1._.exists(valueParser) ? this.beans.expressionService.evaluate(valueParser, params) : newValue;
960 };
961 CellComp.prototype.focusCell = function (forceBrowserFocus) {
962 if (forceBrowserFocus === void 0) { forceBrowserFocus = false; }
963 this.beans.focusedCellController.setFocusedCell(this.gridCell.rowIndex, this.column, this.rowNode.rowPinned, forceBrowserFocus);
964 };
965 CellComp.prototype.setFocusInOnEditor = function () {
966 if (this.editingCell) {
967 if (this.cellEditor && this.cellEditor.focusIn) {
968 // if the editor is present, then we just focus it
969 this.cellEditor.focusIn();
970 }
971 else {
972 // if the editor is not present, it means async cell editor (eg React fibre)
973 // and we are trying to set focus before the cell editor is present, so we
974 // focus the cell instead
975 this.focusCell(true);
976 }
977 }
978 };
979 CellComp.prototype.isEditing = function () {
980 return this.editingCell;
981 };
982 CellComp.prototype.onKeyDown = function (event) {
983 var key = event.which || event.keyCode;
984 // give user a chance to cancel event processing
985 if (this.doesUserWantToCancelKeyboardEvent(event)) {
986 return;
987 }
988 switch (key) {
989 case constants_1.Constants.KEY_ENTER:
990 this.onEnterKeyDown();
991 break;
992 case constants_1.Constants.KEY_F2:
993 this.onF2KeyDown();
994 break;
995 case constants_1.Constants.KEY_ESCAPE:
996 this.onEscapeKeyDown();
997 break;
998 case constants_1.Constants.KEY_TAB:
999 this.onTabKeyDown(event);
1000 break;
1001 case constants_1.Constants.KEY_BACKSPACE:
1002 case constants_1.Constants.KEY_DELETE:
1003 this.onBackspaceOrDeleteKeyPressed(key);
1004 break;
1005 case constants_1.Constants.KEY_DOWN:
1006 case constants_1.Constants.KEY_UP:
1007 case constants_1.Constants.KEY_RIGHT:
1008 case constants_1.Constants.KEY_LEFT:
1009 this.onNavigationKeyPressed(event, key);
1010 break;
1011 }
1012 };
1013 CellComp.prototype.doesUserWantToCancelKeyboardEvent = function (event) {
1014 var callback = this.column.getColDef().suppressKeyboardEvent;
1015 if (utils_1._.missing(callback)) {
1016 return false;
1017 }
1018 else {
1019 // if editing is null or undefined, this sets it to false
1020 var params = {
1021 event: event,
1022 editing: this.editingCell,
1023 column: this.column,
1024 api: this.beans.gridOptionsWrapper.getApi(),
1025 node: this.rowNode,
1026 data: this.rowNode.data,
1027 colDef: this.column.getColDef(),
1028 context: this.beans.gridOptionsWrapper.getContext(),
1029 columnApi: this.beans.gridOptionsWrapper.getColumnApi()
1030 };
1031 return callback(params);
1032 }
1033 };
1034 CellComp.prototype.setFocusOutOnEditor = function () {
1035 if (this.editingCell && this.cellEditor && this.cellEditor.focusOut) {
1036 this.cellEditor.focusOut();
1037 }
1038 };
1039 CellComp.prototype.onNavigationKeyPressed = function (event, key) {
1040 if (this.editingCell) {
1041 this.stopRowOrCellEdit();
1042 }
1043 if (event.shiftKey && this.rangeSelectionEnabled) {
1044 this.onShiftRangeSelect(key);
1045 }
1046 else {
1047 this.beans.rowRenderer.navigateToNextCell(event, key, this.gridCell, true);
1048 }
1049 // if we don't prevent default, the grid will scroll with the navigation keys
1050 event.preventDefault();
1051 };
1052 CellComp.prototype.onShiftRangeSelect = function (key) {
1053 var success = this.beans.rangeController.extendRangeInDirection(this.gridCell, key);
1054 if (!success) {
1055 return;
1056 }
1057 var ranges = this.beans.rangeController.getCellRanges();
1058 // this should never happen, as extendRangeFromCell should always have one range after getting called
1059 if (utils_1._.missing(ranges) || ranges.length !== 1) {
1060 return;
1061 }
1062 var endCell = ranges[0].end;
1063 this.beans.rowRenderer.ensureCellVisible(endCell);
1064 };
1065 CellComp.prototype.onTabKeyDown = function (event) {
1066 if (this.beans.gridOptionsWrapper.isSuppressTabbing()) {
1067 return;
1068 }
1069 this.beans.rowRenderer.onTabKeyDown(this, event);
1070 };
1071 CellComp.prototype.onBackspaceOrDeleteKeyPressed = function (key) {
1072 if (!this.editingCell) {
1073 this.startRowOrCellEdit(key);
1074 }
1075 };
1076 CellComp.prototype.onEnterKeyDown = function () {
1077 if (this.editingCell || this.rowComp.isEditing()) {
1078 this.stopEditingAndFocus();
1079 }
1080 else {
1081 if (this.beans.gridOptionsWrapper.isEnterMovesDown()) {
1082 this.beans.rowRenderer.navigateToNextCell(null, constants_1.Constants.KEY_DOWN, this.gridCell, false);
1083 }
1084 else {
1085 this.startRowOrCellEdit(constants_1.Constants.KEY_ENTER);
1086 }
1087 }
1088 };
1089 CellComp.prototype.navigateAfterEdit = function () {
1090 var fullRowEdit = this.beans.gridOptionsWrapper.isFullRowEdit();
1091 if (fullRowEdit) {
1092 return;
1093 }
1094 var enterMovesDownAfterEdit = this.beans.gridOptionsWrapper.isEnterMovesDownAfterEdit();
1095 if (enterMovesDownAfterEdit) {
1096 this.beans.rowRenderer.navigateToNextCell(null, constants_1.Constants.KEY_DOWN, this.gridCell, false);
1097 }
1098 };
1099 CellComp.prototype.onF2KeyDown = function () {
1100 if (!this.editingCell) {
1101 this.startRowOrCellEdit(constants_1.Constants.KEY_F2);
1102 }
1103 };
1104 CellComp.prototype.onEscapeKeyDown = function () {
1105 if (this.editingCell) {
1106 this.stopRowOrCellEdit(true);
1107 this.focusCell(true);
1108 }
1109 };
1110 CellComp.prototype.onKeyPress = function (event) {
1111 // check this, in case focus is on a (for example) a text field inside the cell,
1112 // in which cse we should not be listening for these key pressed
1113 var eventTarget = utils_1._.getTarget(event);
1114 var eventOnChildComponent = eventTarget !== this.getGui();
1115 if (eventOnChildComponent) {
1116 return;
1117 }
1118 if (!this.editingCell) {
1119 var pressedChar = String.fromCharCode(event.charCode);
1120 if (pressedChar === ' ') {
1121 this.onSpaceKeyPressed(event);
1122 }
1123 else {
1124 if (utils_1._.isEventFromPrintableCharacter(event)) {
1125 this.startRowOrCellEdit(null, pressedChar);
1126 // if we don't prevent default, then the keypress also gets applied to the text field
1127 // (at least when doing the default editor), but we need to allow the editor to decide
1128 // what it wants to do. we only do this IF editing was started - otherwise it messes
1129 // up when the use is not doing editing, but using rendering with text fields in cellRenderer
1130 // (as it would block the the user from typing into text fields).
1131 event.preventDefault();
1132 }
1133 }
1134 }
1135 };
1136 CellComp.prototype.onSpaceKeyPressed = function (event) {
1137 if (!this.editingCell && this.beans.gridOptionsWrapper.isRowSelection()) {
1138 var selected = this.rowNode.isSelected();
1139 this.rowNode.setSelected(!selected);
1140 }
1141 // prevent default as space key, by default, moves browser scroll down
1142 event.preventDefault();
1143 };
1144 CellComp.prototype.onMouseDown = function (mouseEvent) {
1145 // we pass false to focusCell, as we don't want the cell to focus
1146 // also get the browser focus. if we did, then the cellRenderer could
1147 // have a text field in it, for example, and as the user clicks on the
1148 // text field, the text field, the focus doesn't get to the text
1149 // field, instead to goes to the div behind, making it impossible to
1150 // select the text field.
1151 this.focusCell(false);
1152 // if it's a right click, then if the cell is already in range,
1153 // don't change the range, however if the cell is not in a range,
1154 // we set a new range
1155 if (this.beans.rangeController) {
1156 var thisCell = this.gridCell;
1157 if (mouseEvent.shiftKey) {
1158 this.beans.rangeController.extendRangeToCell(thisCell);
1159 }
1160 else {
1161 var cellAlreadyInRange = this.beans.rangeController.isCellInAnyRange(thisCell);
1162 if (!cellAlreadyInRange) {
1163 var ctrlKeyPressed = mouseEvent.ctrlKey || mouseEvent.metaKey;
1164 this.beans.rangeController.setRangeToCell(thisCell, ctrlKeyPressed);
1165 }
1166 }
1167 }
1168 var cellMouseDownEvent = this.createEvent(mouseEvent, events_1.Events.EVENT_CELL_MOUSE_DOWN);
1169 this.beans.eventService.dispatchEvent(cellMouseDownEvent);
1170 };
1171 // returns true if on iPad and this is second 'click' event in 200ms
1172 CellComp.prototype.isDoubleClickOnIPad = function () {
1173 if (!utils_1._.isUserAgentIPad()) {
1174 return false;
1175 }
1176 var nowMillis = new Date().getTime();
1177 var res = nowMillis - this.lastIPadMouseClickEvent < 200;
1178 this.lastIPadMouseClickEvent = nowMillis;
1179 return res;
1180 };
1181 CellComp.prototype.onCellClicked = function (mouseEvent) {
1182 // iPad doesn't have double click - so we need to mimic it do enable editing for
1183 // iPad.
1184 if (this.isDoubleClickOnIPad()) {
1185 this.onCellDoubleClicked(mouseEvent);
1186 mouseEvent.preventDefault(); // if we don't do this, then ipad zooms in
1187 return;
1188 }
1189 var cellClickedEvent = this.createEvent(mouseEvent, events_1.Events.EVENT_CELL_CLICKED);
1190 this.beans.eventService.dispatchEvent(cellClickedEvent);
1191 var colDef = this.column.getColDef();
1192 if (colDef.onCellClicked) {
1193 // to make callback async, do in a timeout
1194 setTimeout(function () { return colDef.onCellClicked(cellClickedEvent); }, 0);
1195 }
1196 var editOnSingleClick = (this.beans.gridOptionsWrapper.isSingleClickEdit() || colDef.singleClickEdit)
1197 && !this.beans.gridOptionsWrapper.isSuppressClickEdit();
1198 if (editOnSingleClick) {
1199 this.startRowOrCellEdit();
1200 }
1201 this.doIeFocusHack();
1202 };
1203 // https://ag-grid.com/forum/showthread.php?tid=4362
1204 // when in IE or Edge, when you are editing a cell, then click on another cell,
1205 // the other cell doesn't keep focus, so navigation keys, type to start edit etc
1206 // don't work. appears that when you update the dom in IE it looses focus
1207 CellComp.prototype.doIeFocusHack = function () {
1208 if (utils_1._.isBrowserIE() || utils_1._.isBrowserEdge()) {
1209 if (utils_1._.missing(document.activeElement) || document.activeElement === document.body) {
1210 // console.log('missing focus');
1211 this.getGui().focus();
1212 }
1213 }
1214 };
1215 CellComp.prototype.createGridCellVo = function () {
1216 var gridCellDef = {
1217 rowIndex: this.rowNode.rowIndex,
1218 floating: this.rowNode.rowPinned,
1219 column: this.column
1220 };
1221 this.gridCell = new gridCell_1.GridCell(gridCellDef);
1222 };
1223 CellComp.prototype.getGridCell = function () {
1224 return this.gridCell;
1225 };
1226 CellComp.prototype.getParentRow = function () {
1227 return this.eParentRow;
1228 };
1229 CellComp.prototype.setParentRow = function (eParentRow) {
1230 this.eParentRow = eParentRow;
1231 };
1232 CellComp.prototype.getColumn = function () {
1233 return this.column;
1234 };
1235 CellComp.prototype.detach = function () {
1236 this.eParentRow.removeChild(this.getGui());
1237 };
1238 // if the row is also getting destroyed, then we don't need to remove from dom,
1239 // as the row will also get removed, so no need to take out the cells from the row
1240 // if the row is going (removing is an expensive operation, so only need to remove
1241 // the top part)
1242 CellComp.prototype.destroy = function () {
1243 _super.prototype.destroy.call(this);
1244 if (this.cellEditor && this.cellEditor.destroy) {
1245 this.cellEditor.destroy();
1246 this.cellEditor = null;
1247 }
1248 if (this.cellRenderer && this.cellRenderer.destroy) {
1249 this.cellRenderer.destroy();
1250 this.cellRenderer = null;
1251 }
1252 };
1253 CellComp.prototype.onLeftChanged = function () {
1254 var left = this.getCellLeft();
1255 this.getGui().style.left = left + 'px';
1256 };
1257 CellComp.prototype.onWidthChanged = function () {
1258 var width = this.getCellWidth();
1259 this.getGui().style.width = width + 'px';
1260 };
1261 CellComp.prototype.getRangeClasses = function () {
1262 var res = [];
1263 if (!this.rangeSelectionEnabled) {
1264 return res;
1265 }
1266 if (this.rangeCount !== 0) {
1267 res.push('ag-cell-range-selected');
1268 }
1269 if (this.rangeCount === 1) {
1270 res.push('ag-cell-range-selected-1');
1271 }
1272 if (this.rangeCount === 2) {
1273 res.push('ag-cell-range-selected-2');
1274 }
1275 if (this.rangeCount === 3) {
1276 res.push('ag-cell-range-selected-3');
1277 }
1278 if (this.rangeCount >= 4) {
1279 res.push('ag-cell-range-selected-4');
1280 }
1281 return res;
1282 };
1283 CellComp.prototype.onRowIndexChanged = function () {
1284 // when index changes, this influences items that need the index, so we update the
1285 // grid cell so they are working off the new index.
1286 this.createGridCellVo();
1287 // when the index of the row changes, ie means the cell may have lost or gained focus
1288 this.onCellFocused();
1289 // check range selection
1290 this.onRangeSelectionChanged();
1291 };
1292 CellComp.prototype.onRangeSelectionChanged = function () {
1293 if (!this.beans.enterprise) {
1294 return;
1295 }
1296 var newRangeCount = this.beans.rangeController.getCellRangeCount(this.gridCell);
1297 var element = this.getGui();
1298 if (this.rangeCount !== newRangeCount) {
1299 utils_1._.addOrRemoveCssClass(element, 'ag-cell-range-selected', newRangeCount !== 0);
1300 utils_1._.addOrRemoveCssClass(element, 'ag-cell-range-selected-1', newRangeCount === 1);
1301 utils_1._.addOrRemoveCssClass(element, 'ag-cell-range-selected-2', newRangeCount === 2);
1302 utils_1._.addOrRemoveCssClass(element, 'ag-cell-range-selected-3', newRangeCount === 3);
1303 utils_1._.addOrRemoveCssClass(element, 'ag-cell-range-selected-4', newRangeCount >= 4);
1304 this.rangeCount = newRangeCount;
1305 }
1306 };
1307 CellComp.prototype.onFirstRightPinnedChanged = function () {
1308 var firstRightPinned = this.column.isFirstRightPinned();
1309 if (this.firstRightPinned !== firstRightPinned) {
1310 this.firstRightPinned = firstRightPinned;
1311 utils_1._.addOrRemoveCssClass(this.getGui(), 'ag-cell-first-right-pinned', firstRightPinned);
1312 }
1313 };
1314 CellComp.prototype.onLastLeftPinnedChanged = function () {
1315 var lastLeftPinned = this.column.isLastLeftPinned();
1316 if (this.lastLeftPinned !== lastLeftPinned) {
1317 this.lastLeftPinned = lastLeftPinned;
1318 utils_1._.addOrRemoveCssClass(this.getGui(), 'ag-cell-last-left-pinned', lastLeftPinned);
1319 }
1320 };
1321 CellComp.prototype.populateTemplate = function () {
1322 if (this.usingWrapper) {
1323 this.eParentOfValue = this.getRefElement('eCellValue');
1324 this.eCellWrapper = this.getRefElement('eCellWrapper');
1325 if (this.includeRowDraggingComponent) {
1326 this.addRowDragging();
1327 }
1328 if (this.includeSelectionComponent) {
1329 this.addSelectionCheckbox();
1330 }
1331 }
1332 else {
1333 this.eParentOfValue = this.getGui();
1334 }
1335 };
1336 CellComp.prototype.addRowDragging = function () {
1337 // row dragging only available in default row model
1338 if (!this.beans.gridOptionsWrapper.isRowModelDefault()) {
1339 utils_1._.doOnce(function () { return console.warn('ag-Grid: row dragging is only allowed in the In Memory Row Model'); }, 'CellComp.addRowDragging');
1340 return;
1341 }
1342 if (this.beans.gridOptionsWrapper.isPagination()) {
1343 utils_1._.doOnce(function () { return console.warn('ag-Grid: row dragging is not possible when doing pagination'); }, 'CellComp.addRowDragging');
1344 return;
1345 }
1346 var rowDraggingComp = new rowDragComp_1.RowDragComp(this.rowNode, this.column, this.getValueToUse(), this.beans);
1347 this.addFeature(this.beans.context, rowDraggingComp);
1348 // let visibleFunc = this.column.getColDef().checkboxSelection;
1349 // visibleFunc = typeof visibleFunc === 'function' ? visibleFunc : null;
1350 // cbSelectionComponent.init({rowNode: this.rowNode, column: this.column, visibleFunc: visibleFunc});
1351 // put the checkbox in before the value
1352 this.eCellWrapper.insertBefore(rowDraggingComp.getGui(), this.eParentOfValue);
1353 };
1354 CellComp.prototype.addSelectionCheckbox = function () {
1355 var cbSelectionComponent = new checkboxSelectionComponent_1.CheckboxSelectionComponent();
1356 this.beans.context.wireBean(cbSelectionComponent);
1357 var visibleFunc = this.column.getColDef().checkboxSelection;
1358 visibleFunc = typeof visibleFunc === 'function' ? visibleFunc : null;
1359 cbSelectionComponent.init({ rowNode: this.rowNode, column: this.column, visibleFunc: visibleFunc });
1360 this.addDestroyFunc(function () { return cbSelectionComponent.destroy(); });
1361 // put the checkbox in before the value
1362 this.eCellWrapper.insertBefore(cbSelectionComponent.getGui(), this.eParentOfValue);
1363 };
1364 CellComp.prototype.addDomData = function () {
1365 var _this = this;
1366 var element = this.getGui();
1367 this.beans.gridOptionsWrapper.setDomData(element, CellComp.DOM_DATA_KEY_CELL_COMP, this);
1368 this.addDestroyFunc(function () {
1369 return _this.beans.gridOptionsWrapper.setDomData(element, CellComp.DOM_DATA_KEY_CELL_COMP, null);
1370 });
1371 };
1372 CellComp.prototype.onCellFocused = function (event) {
1373 var cellFocused = this.beans.focusedCellController.isCellFocused(this.gridCell);
1374 // see if we need to change the classes on this cell
1375 if (cellFocused !== this.cellFocused) {
1376 // if we are not doing cell selection, then the focus class does not change, all cells will
1377 // stay with ag-cell-no-focus class
1378 var doingFocusCss = !this.beans.gridOptionsWrapper.isSuppressCellSelection();
1379 if (doingFocusCss) {
1380 utils_1._.addOrRemoveCssClass(this.getGui(), 'ag-cell-focus', cellFocused);
1381 utils_1._.addOrRemoveCssClass(this.getGui(), 'ag-cell-no-focus', !cellFocused);
1382 }
1383 this.cellFocused = cellFocused;
1384 }
1385 // if this cell was just focused, see if we need to force browser focus, his can
1386 // happen if focus is programmatically set.
1387 if (cellFocused && event && event.forceBrowserFocus) {
1388 this.getGui().focus();
1389 }
1390 // if another cell was focused, and we are editing, then stop editing
1391 var fullRowEdit = this.beans.gridOptionsWrapper.isFullRowEdit();
1392 if (!cellFocused && !fullRowEdit && this.editingCell) {
1393 this.stopRowOrCellEdit();
1394 }
1395 };
1396 // pass in 'true' to cancel the editing.
1397 CellComp.prototype.stopRowOrCellEdit = function (cancel) {
1398 if (cancel === void 0) { cancel = false; }
1399 if (this.beans.gridOptionsWrapper.isFullRowEdit()) {
1400 this.rowComp.stopRowEditing(cancel);
1401 }
1402 else {
1403 this.stopEditing(cancel);
1404 }
1405 };
1406 CellComp.prototype.stopEditing = function (cancel) {
1407 if (cancel === void 0) { cancel = false; }
1408 if (!this.editingCell) {
1409 return;
1410 }
1411 // if no cell editor, this means due to async, that the cell editor never got initialised,
1412 // so we just carry on regardless as if the editing was never started.
1413 if (!this.cellEditor) {
1414 this.editingCell = false;
1415 return;
1416 }
1417 var newValueExists = false;
1418 var newValue;
1419 if (!cancel) {
1420 // also have another option here to cancel after editing, so for example user could have a popup editor and
1421 // it is closed by user clicking outside the editor. then the editor will close automatically (with false
1422 // passed above) and we need to see if the editor wants to accept the new value.
1423 var userWantsToCancel = this.cellEditor.isCancelAfterEnd && this.cellEditor.isCancelAfterEnd();
1424 if (!userWantsToCancel) {
1425 newValue = this.cellEditor.getValue();
1426 newValueExists = true;
1427 }
1428 }
1429 // it is important we set this after setValue() above, as otherwise the cell will flash
1430 // when editing stops. the 'refresh' method checks editing, and doesn't refresh editing cells.
1431 // thus it will skip the refresh on this cell until the end of this method where we call
1432 // refresh directly and we suppress the flash.
1433 this.editingCell = false;
1434 if (this.cellEditor.destroy) {
1435 this.cellEditor.destroy();
1436 }
1437 // important to clear this out - as parts of the code will check for
1438 // this to see if an async cellEditor has yet to be created
1439 this.cellEditor = null;
1440 if (this.cellEditorInPopup) {
1441 this.hideEditorPopup();
1442 this.hideEditorPopup = null;
1443 }
1444 else {
1445 utils_1._.removeAllChildren(this.getGui());
1446 // put the cell back the way it was before editing
1447 if (this.usingWrapper) {
1448 // if wrapper, then put the wrapper back
1449 this.getGui().appendChild(this.eCellWrapper);
1450 }
1451 else {
1452 // if cellRenderer, then put the gui back in. if the renderer has
1453 // a refresh, it will be called. however if it doesn't, then later
1454 // the renderer will be destroyed and a new one will be created.
1455 if (this.cellRenderer) {
1456 // we know it's a dom element (not a string) because we converted
1457 // it after the gui was attached if it was a string.
1458 var eCell = this.cellRendererGui;
1459 // can be null if cell was previously null / contained empty string,
1460 // this will result in new value not being rendered.
1461 if (eCell) {
1462 this.getGui().appendChild(eCell);
1463 }
1464 }
1465 }
1466 }
1467 this.setInlineEditingClass();
1468 if (newValueExists) {
1469 this.rowNode.setDataValue(this.column, newValue);
1470 this.getValueAndFormat();
1471 }
1472 // we suppress the flash, as it is not correct to flash the cell the user has finished editing,
1473 // the user doesn't need to flash as they were the one who did the edit, the flash is pointless
1474 // (as the flash is meant to draw the user to a change that they didn't manually do themselves).
1475 this.refreshCell({ forceRefresh: true, suppressFlash: true });
1476 var event = this.createEvent(null, events_1.Events.EVENT_CELL_EDITING_STOPPED);
1477 this.beans.eventService.dispatchEvent(event);
1478 };
1479 CellComp.DOM_DATA_KEY_CELL_COMP = 'cellComp';
1480 return CellComp;
1481}(component_1.Component));
1482exports.CellComp = CellComp;