@refinitiv-ui/efx-grid 6.0.53 → 6.0.55

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,6 +8,8 @@ declare namespace FieldDefinition {
8
8
 
9
9
  function get(field: string): any;
10
10
 
11
+ function getFieldInfo(field: string): any;
12
+
11
13
  function hasFieldInfo(field: string): boolean;
12
14
 
13
15
  function getTimeSeriesChildren(field: string): any;
@@ -18,8 +20,6 @@ declare namespace FieldDefinition {
18
20
 
19
21
  function setSynapseConfig(config: Grid.SynapseConfig|null): void;
20
22
 
21
- function setFieldCaching(caching: boolean): void;
22
-
23
23
  function disableTimeSeriesExpansion(disabled: boolean): void;
24
24
 
25
25
  function isFormula(field: string): boolean;
@@ -10,9 +10,9 @@ import { Deferred } from "../../tr-grid-util/es6/Deferred.js";
10
10
  * @const
11
11
  */
12
12
  var SYNAPSE_URL =
13
- '/synapse/service/suggestions/suggest/?'
14
- + 'hits=1' // search only 1 result
15
- + '&profile=' + encodeURIComponent('Field Selector');
13
+ "/synapse/service/suggestions/suggest/?"
14
+ + "hits=1" // search only 1 result
15
+ + "&profile=" + encodeURIComponent("Field Selector");
16
16
 
17
17
  /* @namespace */
18
18
  var FieldDefinition = {};
@@ -129,18 +129,13 @@ FieldDefinition._timeSeriesChildren = {};
129
129
  * @private
130
130
  * @const
131
131
  */
132
- FieldDefinition._synapse = '';
132
+ FieldDefinition._synapse = "";
133
133
  /**
134
134
  * @type {string}
135
135
  * @private
136
136
  * @const
137
137
  */
138
- FieldDefinition._lang = 'en';
139
- /**
140
- * @type {boolean}
141
- * @private
142
- */
143
- FieldDefinition._caching = false;
138
+ FieldDefinition._lang = "en";
144
139
  /**
145
140
  * @type {boolean}
146
141
  * @private
@@ -167,17 +162,22 @@ FieldDefinition.set = function(field, def) {
167
162
  }
168
163
  }
169
164
  };
170
- /** @public
165
+ /** Get field definition object
166
+ * @public
171
167
  * @function
172
168
  * @param {string} field
173
169
  * @return {Object}
174
170
  */
175
171
  FieldDefinition.get = function(field) {
176
- if(this._caching) {
177
- return FieldDefinition._defs[field];
178
- }
179
- return null;
172
+ return FieldDefinition._defs[field] || null;
180
173
  };
174
+ /** Get field definition object
175
+ * @public
176
+ * @function
177
+ * @param {string} field
178
+ * @return {Object}
179
+ */
180
+ FieldDefinition.getFieldInfo = FieldDefinition.get;
181
181
 
182
182
  /** @public
183
183
  * @function
@@ -186,7 +186,7 @@ FieldDefinition.get = function(field) {
186
186
  */
187
187
  FieldDefinition.hasFieldInfo = function(field) {
188
188
  var val = FieldDefinition.get(field);
189
- return val && val.field; // Already preventing an error caused by accessing a property on a null value
189
+ return (val && val.field) ? true : false; // Already preventing an error caused by accessing a property on a null value
190
190
  };
191
191
 
192
192
  /** @public
@@ -222,15 +222,6 @@ FieldDefinition.remove = function(field) {
222
222
  */
223
223
  FieldDefinition.setSynapseConfig = function (config) {
224
224
  FieldDefinition._synapse = config;
225
-
226
- };
227
-
228
- /** @public
229
- * @function
230
- * @param {boolean} caching
231
- */
232
- FieldDefinition.setFieldCaching = function (caching) {
233
- FieldDefinition._caching = caching;
234
225
  };
235
226
 
236
227
  /** @public
@@ -355,7 +346,7 @@ FieldDefinition.getFieldProperty = function(field, propertyName) {
355
346
  return null;
356
347
  };
357
348
 
358
- /** to get more info about field via synapse service
349
+ /** To get more info about field via synapse service
359
350
  * @private
360
351
  * @param {string} field
361
352
  * @returns {Promise}
@@ -387,12 +378,12 @@ FieldDefinition.loadFieldInfo = function (field) {
387
378
  // everything fine, call synapse service
388
379
  else {
389
380
  var queryUrl = SYNAPSE_URL
390
- + '&api-key=' + encodeURIComponent(synapse.apiKey)
391
- + '&contextApp=' + encodeURIComponent(synapse.contextApp)
392
- + '&language=' + encodeURIComponent(FieldDefinition._lang)
393
- + '&query=' + encodeURIComponent(field);
381
+ + "&api-key=" + encodeURIComponent(synapse.apiKey)
382
+ + "&contextApp=" + encodeURIComponent(synapse.contextApp)
383
+ + "&language=" + encodeURIComponent(FieldDefinition._lang)
384
+ + "&query=" + encodeURIComponent(field);
394
385
  if (synapse.auth) {
395
- queryUrl += '&auth=' + encodeURIComponent(synapse.auth);
386
+ queryUrl += "&auth=" + encodeURIComponent(synapse.auth);
396
387
  }
397
388
  // TODO: handle client timeout
398
389
  var xmr = new XMLHttpRequest();
@@ -401,9 +392,9 @@ FieldDefinition.loadFieldInfo = function (field) {
401
392
  FieldDefinition._loadingField[field] = defer;
402
393
 
403
394
  // for now we care only successful case
404
- xmr.addEventListener('loadend', onLoadEnd);
405
- xmr.addEventListener('timeout', onError);
406
- xmr.addEventListener('error', onError);
395
+ xmr.addEventListener("loadend", onLoadEnd);
396
+ xmr.addEventListener("timeout", onError);
397
+ xmr.addEventListener("error", onError);
407
398
  xmr.open("GET", queryUrl, true); // true for asynchronous
408
399
  xmr.send();
409
400
  }
@@ -431,7 +422,7 @@ FieldDefinition._mockOnLoadEndData = function (field, defer) {
431
422
  var fieldUpperCase = field.toUpperCase();
432
423
  var fieldLowerCase = fieldUpperCase.toLowerCase();
433
424
  var label;
434
- if (fieldLowerCase.indexOf('_') === 2) { // transform XX_ABCD -> Abcd
425
+ if (fieldLowerCase.indexOf("_") === 2) { // transform XX_ABCD -> Abcd
435
426
  label = fieldUpperCase[3] + fieldLowerCase.substring(4);
436
427
  } else {
437
428
  label = fieldUpperCase[0] + fieldLowerCase.substring(1);
@@ -493,49 +484,49 @@ FieldDefinition._mockOnLoadEndData = function (field, defer) {
493
484
  function onLoadEnd(e) {
494
485
  var xmr = e.currentTarget;
495
486
  var defer = xmr._defer;
496
- var resolve = defer.resolve;
497
- var reject = defer.reject;
487
+ var field = xmr._field;
498
488
 
499
- delete FieldDefinition._loadingField[xmr._field];
489
+ delete FieldDefinition._loadingField[field];
500
490
 
501
491
  if (xmr.status === 200) { // case success
502
- var returnObj = {
503
- field: xmr._field,
504
- fieldDefinition: null
505
- };
506
492
  var res = JSON.parse(xmr.responseText);
493
+ var fieldDef = null;
507
494
 
508
495
  var result = res.result && res.result[0];
509
496
  if (result) {
510
497
  var item = result.hits && result.hits[0];
511
498
  if (item && item.p) {
512
499
  var profile = item.p;
513
- if (profile.fn.toUpperCase() === xmr._field.toUpperCase()) {
514
- var fieldDef = FieldDefinition.get(xmr._field) || {};
500
+ if (profile.fn.toUpperCase() === field.toUpperCase()) {
501
+ fieldDef = FieldDefinition.get(field) || {};
515
502
 
516
503
  fieldDef.field = profile.fn; // name
504
+ fieldDef.name = profile.fl; // label
517
505
  fieldDef.rank = profile.Rank;
518
506
  fieldDef.fieldDataType = profile.fdt; // data type
507
+ fieldDef.id = profile.fid;
519
508
  fieldDef.IsMonitorOnlyField = profile.fimo;
520
509
  fieldDef.IsRealtimePortfolioField = profile.firp;
521
510
  fieldDef.IsRealtimeField = profile.firt;
522
- fieldDef.name = profile.fl; // label
523
511
  fieldDef.source = profile.fsrc;
524
512
  fieldDef.sourceRank = profile.fsrnk;
525
513
  fieldDef.description = item.title;
526
514
 
527
- FieldDefinition.set(xmr._field, fieldDef);
528
- returnObj.fieldDefinition = fieldDef;
515
+ FieldDefinition.set(field, fieldDef);
529
516
  }
530
517
  }
531
518
  }
532
- resolve(returnObj);
519
+ if(fieldDef) {
520
+ defer.resolve(fieldDef);
521
+ } else {
522
+ defer.reject("No definition for " + field);
523
+ }
533
524
  } else if (xmr._error) { // case error && time out
534
- xmr._error.field = xmr._field;
535
- reject(xmr._error);
525
+ xmr._error.field = field;
526
+ defer.reject(xmr._error);
536
527
  } else { // case status not 200
537
- reject({
538
- field: xmr._field,
528
+ defer.reject({
529
+ field: field,
539
530
  status: xmr.status,
540
531
  statusText: xmr.statusText,
541
532
  request: xmr
@@ -133,9 +133,13 @@ declare class Grid extends EventDispatcher {
133
133
 
134
134
  public replaceColumn(columnOption: ColumnDefinition.Options|string|null, colRef: Grid.ColumnReference|null): void;
135
135
 
136
+ public getFieldInfo(field: string): any;
137
+
138
+ public loadFieldInfo(field: string): Promise<any>|null;
139
+
136
140
  public setColumns(columns: (any)[]|null): void;
137
141
 
138
- public restoreColumns(columns: (any)[]|null): void;
142
+ public restoreColumns(columns: (any)[]|null, byId?: boolean|null): void;
139
143
 
140
144
  public setFields(ary: (string)[]|null): void;
141
145
 
@@ -198,15 +198,6 @@ var cloneRowData = function(fromRowDef, toRowDef) {
198
198
  }
199
199
  };
200
200
 
201
- /** @private
202
- * @param {string} sortField
203
- * @param {Object} elemData
204
- * @param {number} index
205
- */
206
- var mapRowOrder = function (sortField, elemData, index) { // edit name
207
- elemData[sortField] = index; // Make column for sort with user data array
208
- };
209
-
210
201
  /** @private
211
202
  * @param {RowDefinition} rowDef
212
203
  * @return {boolean}
@@ -291,6 +282,19 @@ var _hasFieldOrId = function(colDef, str) {
291
282
  return (colDef.getField() === str) || (colDef.getId() === str);
292
283
  };
293
284
 
285
+ /** Compare the difference in the 'id' property.
286
+ * @private
287
+ * @param {Object} obj1
288
+ * @param {Object} obj2
289
+ * @returns {boolean} If the id property of two objects is equal, the return will be true, otherwise it will be false.
290
+ */
291
+ var _hasMatchingId = function(obj1, obj2) {
292
+ if(!obj1 || !obj2 || !obj1.id || !obj2.id) { // Handle nullable, if the object or id have null, it's means difference value
293
+ return false;
294
+ }
295
+ return obj1.id === obj2.id;
296
+ };
297
+
294
298
  /** @constructor
295
299
  * @extends {EventDispatcher}
296
300
  * @param {(Element|null)=} placeholder
@@ -840,7 +844,6 @@ Grid.prototype.initialize = function(gridOption) {
840
844
 
841
845
  if (gridOption["fieldCaching"]) {
842
846
  t._fieldCaching = gridOption["fieldCaching"];
843
- FieldDefinition.setFieldCaching(t._fieldCaching);
844
847
  }
845
848
 
846
849
  if(gridOption["timeSeriesExpansion"] != null) {
@@ -1590,8 +1593,8 @@ Grid.prototype.replaceColumn = function (columnOption, colRef) {
1590
1593
  * @param {Object} response
1591
1594
  */
1592
1595
  Grid.prototype._onFieldLoadedSuccess = function (field, colDef, response) {
1593
- if (response && response.fieldDefinition) {
1594
- var fieldDef = response.fieldDefinition;
1596
+ if (response && response.id) {
1597
+ var fieldDef = response;
1595
1598
  if (colDef && colDef.getField() === field) {
1596
1599
  if (colDef.isDefaultName() && fieldDef.name) {
1597
1600
  colDef.setName(fieldDef.name);
@@ -1639,6 +1642,34 @@ Grid.prototype._setScrollbarParent = function (host) {
1639
1642
  this._grid.getHScrollbar().attachToExternalElement(host);
1640
1643
  };
1641
1644
 
1645
+ /** Get stored field information. If field information has not been requested or no data has been received yet, null value is returned.
1646
+ * @public
1647
+ * @function
1648
+ * @param {string} field
1649
+ * @return {Object}
1650
+ */
1651
+ Grid.prototype.getFieldInfo = function(field) {
1652
+ return FieldDefinition.getFieldInfo(field);
1653
+ };
1654
+ /** Request field information from Synapse service. If field information already exists, a resolved promise is returned. Synapse config must be supplied before the request can be made.
1655
+ * @public
1656
+ * @function
1657
+ * @param {string} field
1658
+ * @return {Promise}
1659
+ * @example
1660
+ * var gridConfig = {
1661
+ * synapse: { // define synapse configuration
1662
+ * apiKey: "xxx",
1663
+ * contextApp: "xxx",
1664
+ * auth: "xxx" (optional)
1665
+ * }
1666
+ * };
1667
+ * var promise = grid.loadFieldInfo("CF_LAST");
1668
+ */
1669
+ Grid.prototype.loadFieldInfo = function(field) {
1670
+ return FieldDefinition.loadFieldInfo(field);
1671
+ };
1672
+
1642
1673
  /**
1643
1674
  * @private
1644
1675
  * @param {string} field
@@ -1697,8 +1728,9 @@ Grid.prototype.setColumns = function(columns) {
1697
1728
  /** Remove, add and keep column based on the given column data
1698
1729
  * @public
1699
1730
  * @param {Array.<Object>} columns Array of column options
1731
+ * @param {boolean=} byId=false, if enable it, this method will only check for differences in the 'id' property
1700
1732
  */
1701
- Grid.prototype.restoreColumns = function(columns) {
1733
+ Grid.prototype.restoreColumns = function(columns, byId) {
1702
1734
  var grid = this._grid;
1703
1735
  grid.startBatch("reset");
1704
1736
  var configObj = this.getConfigObject();
@@ -1707,6 +1739,7 @@ Grid.prototype.restoreColumns = function(columns) {
1707
1739
  var preColLen = previousColumns.length;
1708
1740
  var newColLen = columns.length;
1709
1741
 
1742
+ var compareLogic = byId ? _hasMatchingId : deepEqual;
1710
1743
  var removingFields = [];
1711
1744
  var keepingColumns = [];
1712
1745
  var columnOrdering = [];
@@ -1716,7 +1749,7 @@ Grid.prototype.restoreColumns = function(columns) {
1716
1749
  for (i = 0; i < preColLen; i++) {
1717
1750
  found = false;
1718
1751
  for (j = 0; j < newColLen; j++) {
1719
- if (deepEqual(previousColumns[i], columns[j])) {
1752
+ if(compareLogic(previousColumns[i], columns[j])) {
1720
1753
  keepingColumns.push(previousColumns[i]);
1721
1754
  found = true;
1722
1755
  break;
@@ -1737,7 +1770,7 @@ Grid.prototype.restoreColumns = function(columns) {
1737
1770
  for (i = 0; i < newColLen; i++) {
1738
1771
  found = false;
1739
1772
  for (j = 0; j < keepingLen; j++) { // loop only keeping column
1740
- if (deepEqual(columns[i], keepingColumns[j])) {
1773
+ if(compareLogic(columns[i], keepingColumns[j])) {
1741
1774
  found = true;
1742
1775
  var colIndex = this.getColumnIndex(columns[i].field); // We cannot use 'i' (colIndex) in this case, as it will sort the columns. Instead, we need to obtain a new column index from the field.
1743
1776
  columnOrdering.push(this.getColumnId(colIndex));
@@ -2416,20 +2449,11 @@ Grid.prototype.updateDataSet = function(records, rowIdentifier) {
2416
2449
  }
2417
2450
 
2418
2451
  // Map new data index
2419
- var newDataMap = {};
2420
2452
  var recordCount = records.length;
2421
- var record, i;
2422
- for (i = 0; i < recordCount; i++) {
2423
- record = records[i];
2424
- newDataMap[record[rowIdentifier]] = record; // Assign a new data map to compare to the previous data
2425
- }
2426
-
2427
2453
  var fieldSorting = "ROW_ORDER"; // TODO: Should be config by options
2428
- records.forEach(mapRowOrder.bind(null, fieldSorting));
2429
-
2430
2454
  var oldDataMap = {};
2431
- var rowDef, id;
2432
- var rowDefs = this.getRowDefinitions(); // WARNING: Filtered and hidden rows are not included
2455
+ var rowDef, id, record, i;
2456
+ var rowDefs = this.getAllRowDefinitions(); // Include the filter/hidden rows
2433
2457
  var rowDefCount = rowDefs.length;
2434
2458
  for (i = 0; i < rowDefCount; i++) {
2435
2459
  rowDef = rowDefs[i];
@@ -2443,26 +2467,35 @@ Grid.prototype.updateDataSet = function(records, rowIdentifier) {
2443
2467
  }
2444
2468
  }
2445
2469
 
2446
- // Check Remove previous data
2447
- for (var rowIdentifierName in oldDataMap) {
2448
- if (oldDataMap[rowIdentifierName] && !newDataMap[rowIdentifierName]) {
2449
- this.removeRow(oldDataMap[rowIdentifierName]); // Slow
2450
- }
2451
- }
2452
-
2453
2470
  // Check Update and Insert
2454
- for (i = 0; i < recordCount; i++) {
2471
+ var idMap = {};
2472
+ var newDataMap = {};
2473
+ for (i = recordCount - 1; i >= 0; i--) {
2455
2474
  record = records[i];
2456
2475
  id = record[rowIdentifier];
2457
2476
  rowDef = oldDataMap[id];
2477
+ newDataMap[id] = record; // Assign a new data map to compare to the previous data
2478
+ record[fieldSorting] = i;
2479
+ if(idMap[id]) { // Prevent assign data in duplicate row
2480
+ continue;
2481
+ }
2482
+ idMap[id] = true;
2458
2483
  if (!rowDef) {
2459
- this.insertRow({ values: newDataMap[id]}); // Insert last position
2484
+ this.insertRow({ values: record}); // Insert last position
2460
2485
  } else {
2461
2486
  rowDef.setRowData(record);
2462
2487
  }
2463
2488
  }
2489
+
2490
+ // Check Remove previous data
2491
+ for (var rowIdentifierName in oldDataMap) {
2492
+ if (oldDataMap[rowIdentifierName] && !newDataMap[rowIdentifierName]) {
2493
+ this.removeRow(oldDataMap[rowIdentifierName]); // Slow
2494
+ }
2495
+ }
2496
+
2464
2497
  // Sorting
2465
- this._dt.sortOnce("ROW_DEF", "a", compareNumber, fieldSorting);
2498
+ this._dt.sortOnce(ROW_DEF, "a", compareNumber, fieldSorting);
2466
2499
  };
2467
2500
  /** @private
2468
2501
  * @param {Array|Object} item
@@ -702,7 +702,7 @@ StatisticsRowPlugin.prototype._onPostSectionDataBinding = function (e) {
702
702
  }
703
703
  // Render stuff
704
704
  for(c = 0; c < colCount; ++c) {
705
- this._renderStatistics(c, statistics[c], e.actualUpdate);
705
+ this._renderStatistics(c, statistics[c], e ? e.actualUpdate : false);
706
706
  }
707
707
 
708
708
  if(this.hasListener("postRendering")) {
@@ -4,22 +4,22 @@ import { GridPlugin } from "../../tr-grid-util/es6/GridPlugin.js";
4
4
  declare namespace AutoTooltipPlugin {
5
5
 
6
6
  type Options = {
7
- header?: boolean,
8
- title?: boolean,
9
- footer?: boolean,
10
- content?: boolean,
11
- quickMode?: boolean
7
+ header?: boolean|null,
8
+ title?: boolean|null,
9
+ footer?: boolean|null,
10
+ content?: boolean|null,
11
+ quickMode?: boolean|null
12
12
  };
13
13
 
14
14
  type ColumnOptions = {
15
- autoTooltip?: boolean
15
+ autoTooltip?: boolean|null
16
16
  };
17
17
 
18
18
  }
19
19
 
20
20
  declare class AutoTooltipPlugin extends GridPlugin {
21
21
 
22
- constructor(options?: AutoTooltipPlugin.Options);
22
+ constructor(options?: AutoTooltipPlugin.Options|null);
23
23
 
24
24
  public getName(): string;
25
25
 
@@ -31,9 +31,9 @@ declare class AutoTooltipPlugin extends GridPlugin {
31
31
 
32
32
  public getConfigObject(gridOptions?: any): any;
33
33
 
34
- public applyTooltip(colIndex: number, fromR?: number, toR?: number): void;
34
+ public applyTooltip(colIndex: number, fromR?: number|null, toR?: number|null): void;
35
35
 
36
- public applyTooltipToColumns(colIndices?: (number)[]): void;
36
+ public applyTooltipToColumns(colIndices?: (number)[]|null): void;
37
37
 
38
38
  public applyTooltipToAllColumns(): void;
39
39
 
@@ -279,12 +279,14 @@ AutoTooltipPlugin.prototype.applyTooltipToAllColumns = function () {
279
279
  AutoTooltipPlugin.prototype._applyTooltipToSection = function (section, colIndex, fromR, toR) {
280
280
  if (!section) { return false; }
281
281
 
282
- if (!fromR) {
282
+ if (fromR == null) {
283
+ fromR = section.getFirstIndexInView();
284
+ } else if(!fromR) {
283
285
  fromR = 0;
284
286
  }
285
287
 
286
288
  if (toR == null) {
287
- toR = section.getRowCount();
289
+ toR = section.getLastIndexInView() + 1;
288
290
  }
289
291
 
290
292
  if (fromR >= toR) {
@@ -296,6 +298,10 @@ AutoTooltipPlugin.prototype._applyTooltipToSection = function (section, colIndex
296
298
  return false;
297
299
  }
298
300
 
301
+ if(columnObj.getVisibility && !columnObj.getVisibility()) {
302
+ return false;
303
+ }
304
+
299
305
  var colElem = columnObj.getElement();
300
306
  if (!colElem) {
301
307
  return false;
@@ -93,6 +93,8 @@ declare class ColumnGroupingPlugin extends GridPlugin {
93
93
 
94
94
  public unpinGroup(groupId: string, dest?: (number|string)|null): void;
95
95
 
96
+ public selectGroupHeaders(colIndices: (number)[]|null): void;
97
+
96
98
  public static getObjectIndex(column: any): number;
97
99
 
98
100
  public static getObjectId(column: any): string;
@@ -71,6 +71,10 @@ ColumnGroupingPlugin.prototype._rerenderTimerId = 0;
71
71
  /** @type {number}
72
72
  * @private
73
73
  */
74
+ ColumnGroupingPlugin.prototype._groupHeaderTimerId = 0;
75
+ /** @type {number}
76
+ * @private
77
+ */
74
78
  ColumnGroupingPlugin.prototype._addingTimerId = 0;
75
79
  /** @type {Array}
76
80
  * @private
@@ -84,6 +88,11 @@ ColumnGroupingPlugin.prototype._legacyColumnGroups = null;
84
88
  * @private
85
89
  */
86
90
  ColumnGroupingPlugin.prototype._inPinning = false;
91
+ /** @type {!Object.<string, boolean>}
92
+ * @description A map of selected groups
93
+ * @private
94
+ */
95
+ ColumnGroupingPlugin.prototype._selectedColMap;
87
96
 
88
97
  /** @private
89
98
  * @param {number} a
@@ -298,6 +307,10 @@ ColumnGroupingPlugin.prototype.unload = function (host) {
298
307
  clearTimeout(this._rerenderTimerId);
299
308
  this._rerenderTimerId = 0;
300
309
  }
310
+ if (this._groupHeaderTimerId) {
311
+ clearTimeout(this._groupHeaderTimerId);
312
+ this._groupHeaderTimerId = 0;
313
+ }
301
314
  if (this._addingTimerId) {
302
315
  clearTimeout(this._addingTimerId);
303
316
  this._addingTimerId = 0;
@@ -485,6 +498,7 @@ ColumnGroupingPlugin.prototype._applyGrouping = function () {
485
498
  if (cell) {
486
499
  // TODO: The style and class from the user will not be reset, for example, the color or background
487
500
  cell.removeClass("no-sort");
501
+ cell.removeClass("selected-group");
488
502
  cell.removeAttribute("group-id");
489
503
  }
490
504
  }
@@ -655,6 +669,14 @@ ColumnGroupingPlugin.prototype._requestApplyGrouping = function () {
655
669
  this._rerenderTimerId = setTimeout(this._applyGrouping.bind(this), 10);
656
670
  }
657
671
  };
672
+ /** Set a timer to call renderGroups only once to avoid performance issue
673
+ * @private
674
+ */
675
+ ColumnGroupingPlugin.prototype._requestGroupRendering = function () {
676
+ if (!this._groupHeaderTimerId) {
677
+ this._groupHeaderTimerId = setTimeout(this.renderGroups.bind(this), 10);
678
+ }
679
+ };
658
680
  /** Apply nearest grouping to column.
659
681
  * @private
660
682
  * @param {number} colIndex
@@ -753,11 +775,16 @@ ColumnGroupingPlugin.prototype._spanGroupHorizontally = function (titleSection)
753
775
  spanIndices = [];
754
776
  groupDef = visibleGroupMap[id];
755
777
  colIds = GroupDefinitions.getLeafDescendants(visibleGroupMap, id);
778
+ var isAllSelected = this._selectedColMap ? true : false;
756
779
  for (i = 0; i < colIds.length; i++) {
757
- index = this._getColumnIndexById(colIds[i]);
780
+ var colId = colIds[i];
781
+ index = this._getColumnIndexById(colId);
758
782
  if (index > -1) {
759
783
  spanIndices.push(index);
760
784
  }
785
+ if (isAllSelected && this._selectedColMap) {
786
+ isAllSelected &= this._selectedColMap[colId];
787
+ }
761
788
  }
762
789
  spanIndices.sort(ColumnGroupingPlugin._ascSortLogic);
763
790
  len = spanIndices.length;
@@ -767,6 +794,10 @@ ColumnGroupingPlugin.prototype._spanGroupHorizontally = function (titleSection)
767
794
  var end = spanIndices[len - 1];
768
795
  section.setCellColSpan(start, groupDef["onRow"], end - start + 1);
769
796
  groupDef["colIndex"] = start;
797
+ if (isAllSelected) {
798
+ var cell = section.getCell(start, groupDef["onRow"]);
799
+ cell.addClass("selected-group");
800
+ }
770
801
  }
771
802
  }
772
803
  };
@@ -774,6 +805,7 @@ ColumnGroupingPlugin.prototype._spanGroupHorizontally = function (titleSection)
774
805
  * @public
775
806
  */
776
807
  ColumnGroupingPlugin.prototype.renderGroups = function () {
808
+ this._groupHeaderTimerId = 0;
777
809
  var hostCount = this._hosts.length;
778
810
  var groupMap = this._visibleGroupMap;
779
811
  for (var i = 0; i < hostCount; ++i) {
@@ -798,6 +830,17 @@ ColumnGroupingPlugin.prototype._renderGroup = function (groupDef, section) {
798
830
  }
799
831
  var rowIndex = groupDef["onRow"];
800
832
  var cell = section.getCell(colIndex, rowIndex);
833
+ var colIds = GroupDefinitions.getLeafDescendants(this._visibleGroupMap, groupDef["id"]);
834
+ var isAllSelected = false;
835
+ if (this._selectedColMap) {
836
+ isAllSelected = true;
837
+ for (var i = 0; i < colIds.length; i++) {
838
+ if (!this._selectedColMap[colIds[i]]) {
839
+ isAllSelected = false;
840
+ break;
841
+ }
842
+ }
843
+ }
801
844
  if (cell) {
802
845
  // Overide the defaults
803
846
  cell.setStyle("text-align", groupDef["alignment"] || "");
@@ -807,6 +850,11 @@ ColumnGroupingPlugin.prototype._renderGroup = function (groupDef, section) {
807
850
  // Additional cell settings must be removed in the _applyGrouping() method
808
851
  cell.addClass("no-sort");
809
852
  cell.setAttribute("group-id", groupDef["id"]);
853
+ if (isAllSelected) {
854
+ cell.addClass("selected-group");
855
+ } else {
856
+ cell.removeClass("selected-group");
857
+ }
810
858
  if (groupDef["legacyRender"]) {
811
859
  // Built-in version if render receive colIndex, cell, groupDefinition as arguments
812
860
  groupDef["legacyRender"](colIndex, cell, groupDef);
@@ -1710,6 +1758,26 @@ ColumnGroupingPlugin.prototype.unpinGroup = function (groupId, dest) {
1710
1758
  this.moveGroup(groupId, destId);
1711
1759
  }
1712
1760
  };
1761
+ /** @public
1762
+ * @param {Array<number>} colIndices
1763
+ */
1764
+ ColumnGroupingPlugin.prototype.selectGroupHeaders = function (colIndices) {
1765
+ var host = this._hosts[0];
1766
+ var count = colIndices.length;
1767
+ if (host && count) {
1768
+ var colIds = [];
1769
+ for (var i = 0; i < count; i++) {
1770
+ colIds.push(host.getColumnId(colIndices[i]));
1771
+ }
1772
+ var colMap = host.createColumnMap(colIds);
1773
+ if (colMap) {
1774
+ this._selectedColMap = colMap;
1775
+ }
1776
+ } else {
1777
+ this._selectedColMap = null;
1778
+ }
1779
+ this._requestGroupRendering();
1780
+ };
1713
1781
 
1714
1782
  /** Get column index from column object
1715
1783
  * @public