@refinitiv-ui/efx-grid 6.0.99 → 6.0.101

Sign up to get free protection for your applications and to get access to all the features.
@@ -43,7 +43,8 @@ declare namespace ColumnDefinition {
43
43
  stationary?: boolean|null,
44
44
  leftPinned?: boolean|null,
45
45
  rightPinned?: boolean|null,
46
- info?: any
46
+ info?: any,
47
+ focusable?: boolean|null
47
48
  };
48
49
 
49
50
  }
@@ -158,6 +159,8 @@ declare class ColumnDefinition {
158
159
 
159
160
  public getColumnInfo(): any;
160
161
 
162
+ public isFocusable(): boolean;
163
+
161
164
  }
162
165
 
163
166
  declare const COL_DEF: string;
@@ -50,6 +50,7 @@ import Engine from "../../tr-grid-util/es6/formula/Engine.js";
50
50
  * @property {boolean=} leftPinned=false If enabled, the column will not be part of the scrollable area and is pinned to the left side
51
51
  * @property {boolean=} rightPinned=false If enabled, the column will not be part of the scrollable area and is pinned to the right side
52
52
  * @property {Object=} info=null For storing any additional information to the column
53
+ * @property {boolean=} focusable=false If enabled, the column will be used to find focusable element when pressing tab key
53
54
  */
54
55
 
55
56
  /** mapping of field type to javascript type
@@ -258,6 +259,10 @@ ColumnDefinition.prototype._info = null;
258
259
  * @private
259
260
  */
260
261
  ColumnDefinition.prototype._coreColDef = null;
262
+ /** @type {boolean}
263
+ * @private
264
+ */
265
+ ColumnDefinition.prototype._focusable = false;
261
266
  //#endregion Private Members
262
267
 
263
268
 
@@ -402,6 +407,11 @@ ColumnDefinition.prototype.initialize = function(columnOption) {
402
407
  this._sortable = val ? true : false;
403
408
  }
404
409
 
410
+ val = columnOption["focusable"];
411
+ if(val != null) {
412
+ this._focusable = val ? true : false;
413
+ }
414
+
405
415
  val = columnOption["className"] || columnOption["class"];
406
416
  if(typeof val === "string") {
407
417
  this._classes = val.split(" ");
@@ -785,6 +795,10 @@ ColumnDefinition.prototype.getConfigObject = function(colOptions) {
785
795
  obj["autoGenerated"] = this._autoGenerated;
786
796
  }
787
797
 
798
+ if(this._focusable) {
799
+ obj["focusable"] = true;
800
+ }
801
+
788
802
  let core = this._eventArg["core"];
789
803
  let grid = this._eventArg["grid"];
790
804
  let colIndex = grid.getColumnIndex(this);
@@ -1149,6 +1163,12 @@ ColumnDefinition.prototype._setCoreColumnDef = function(obj) {
1149
1163
  this._coreColDef = obj || null;
1150
1164
  };
1151
1165
 
1166
+ /** @public
1167
+ * @return {boolean}
1168
+ */
1169
+ ColumnDefinition.prototype.isFocusable = function() {
1170
+ return this._focusable;
1171
+ };
1152
1172
 
1153
1173
  export {ColumnDefinition, COL_DEF};
1154
1174
  export default ColumnDefinition;
@@ -250,6 +250,8 @@ declare class Grid extends EventDispatcher {
250
250
 
251
251
  public setRicData(ric: string, values: any): void;
252
252
 
253
+ public getAllRics(): (string)[]|null;
254
+
253
255
  public setRowData(rowRef: Grid.RowReference|null, values: any): void;
254
256
 
255
257
  public setStaticRowData(rowRef: Grid.RowReference|null, values: any): void;
@@ -343,6 +343,11 @@ let Grid = function(placeholder, config) {
343
343
  t._snapshotFillerDataChanged = t._snapshotFillerDataChanged.bind(t);
344
344
  t._onPollingInterval = t._onPollingInterval.bind(t);
345
345
 
346
+ t._onKeyDown = t._onKeyDown.bind(t);
347
+ t._requestScroll = t._requestScroll.bind(t);
348
+ t._onVScroll = t._onVScroll.bind(t);
349
+ t._selfScrollToRow = t._selfScrollToRow.bind(t);
350
+
346
351
  t._streamingConflator = new Conflator(50, t._updateStreamingData);
347
352
  t._formulaConflator = new Conflator(300, t._onFormulaDataChanged);
348
353
  t._chainConflator = new Conflator(100, t._addMemberOfChain);
@@ -422,6 +427,10 @@ let Grid = function(placeholder, config) {
422
427
  t._grid.listen("postSectionDataBinding", t._onPostSectionDataBinding);
423
428
  t._grid.listen("firstRendered", t._dispatch.bind(t, "firstRendered"));
424
429
  t._grid.listen("afterContentBinding", t._dispatch.bind(t, "afterContentBinding"));
430
+ t._grid.listen("keydown", t._onKeyDown);
431
+
432
+ t._grid.getVScrollbar().listen("scroll", t._onVScroll);
433
+ t._hiddenInput = t._grid.getHiddenInput();
425
434
 
426
435
  t._grid.enableRowHighlighting(true);
427
436
 
@@ -607,7 +616,14 @@ Grid.prototype._childDataField = "";
607
616
  * @private
608
617
  */
609
618
  Grid.prototype._topSection = true;
610
-
619
+ /** @type {Object}
620
+ * @private
621
+ */
622
+ Grid.prototype._focusingArgs = null;
623
+ /** @type {number}
624
+ * @private
625
+ */
626
+ Grid.prototype._scrolledRow = -1;
611
627
  /** @public
612
628
  */
613
629
  Grid.prototype.dispose = function() {
@@ -640,6 +656,11 @@ Grid.prototype.dispose = function() {
640
656
  this._subs["dispose"]();
641
657
  this._subs = null;
642
658
  }
659
+
660
+ if(this._focusingArgs && this._focusingArgs.id) {
661
+ clearTimeout(this._focusingArgs.id);
662
+ this._focusingArgs = null;
663
+ }
643
664
  };
644
665
  /** @public
645
666
  * @return {Element}
@@ -3067,6 +3088,13 @@ Grid.prototype.setRicData = function(ric, values) {
3067
3088
  }
3068
3089
  }
3069
3090
  };
3091
+ /** Retrieve all RIC subscriptions. This method is not dependent on the number of rows, the RIC can vary from the current row in the grid.
3092
+ * @public
3093
+ * @return {Array.<string>}
3094
+ */
3095
+ Grid.prototype.getAllRics = function() {
3096
+ return this._connector.getAllRics();
3097
+ };
3070
3098
  /** A shorthand to set row data based on index of the specified row. It is better to keep rowDefinition object for updating data directly as row index can be changed by sorting and filtering.
3071
3099
  * @public
3072
3100
  * @param {Grid~RowReference} rowRef
@@ -4132,5 +4160,198 @@ Grid.prototype.getVScrollView = function () {
4132
4160
  return this._grid.getVScrollView();
4133
4161
  };
4134
4162
 
4163
+ /** @private
4164
+ * @param {Element} el
4165
+ * @return {boolean}
4166
+ */
4167
+ function isValidInput(el) {
4168
+ return el && el.tagName !== "SPAN" && !el.disabled;
4169
+ }
4170
+ /** @private
4171
+ * @param {Element} el
4172
+ * @param {Element} hiddenInput
4173
+ * @return {boolean}
4174
+ */
4175
+ function isValidTarget(el, hiddenInput) {
4176
+ return !el.classList.contains("valigner") && el !== hiddenInput;
4177
+ }
4178
+ /** @private
4179
+ * @param {Object} cell
4180
+ * @return {boolean}
4181
+ */
4182
+ function focusCell(cell) {
4183
+ if(cell) {
4184
+ let cellContent = cell.getContent();
4185
+ if(cellContent && isValidInput(cellContent)) {
4186
+ cellContent.focus();
4187
+ return true;
4188
+ }
4189
+ }
4190
+ return false;
4191
+ }
4192
+ /** @private
4193
+ */
4194
+ Grid.prototype._onVScroll = function() {
4195
+ let args = this._focusingArgs;
4196
+ if(!args) { return; }
4197
+
4198
+ this._focusingArgs = null;
4199
+ let event = args.event;
4200
+ let cell = args.section.getCell(args.colIndex, args.rowIndex);
4201
+ if(focusCell(cell)) {
4202
+ event.preventDefault();
4203
+ } else {
4204
+ if(event.shiftKey) {
4205
+ this._findPrevFocusableCell(event, args.colIndex, args.rowIndex, args.focusableColIndices);
4206
+ } else {
4207
+ this._findNextFocusableCell(event, args.colIndex, args.rowIndex, args.focusableColIndices);
4208
+ }
4209
+ }
4210
+ };
4211
+ /** @private
4212
+ */
4213
+ Grid.prototype._selfScrollToRow = function() {
4214
+ let args = this._focusingArgs;
4215
+ if(!args) { return; }
4216
+ args.id = 0;
4217
+ this.scrollToRow(args.rowIndex);
4218
+ };
4219
+ /** @private
4220
+ * @param {Object} e
4221
+ * @param {number} colIndex
4222
+ * @param {number} rowIndex
4223
+ * @param {Array} focusableColIndices
4224
+ * @param {Object} section
4225
+ */
4226
+ Grid.prototype._requestScroll = function(e, colIndex, rowIndex, focusableColIndices, section) {
4227
+ let args = this._focusingArgs;
4228
+ if(args) { return; }
4229
+ if(this._scrolledRow === rowIndex) { return; } // Avoid infinite loop
4230
+
4231
+ this._scrolledRow = rowIndex;
4232
+ args = this._focusingArgs = {
4233
+ event: e,
4234
+ colIndex: colIndex,
4235
+ rowIndex: rowIndex,
4236
+ focusableColIndices: focusableColIndices,
4237
+ section: section
4238
+ };
4239
+
4240
+ args.id = setTimeout(this._selfScrollToRow); // Avoid event loop protection
4241
+ };
4242
+ /** @private
4243
+ * @param {Object} e
4244
+ * @param {number} colIndex
4245
+ * @param {number} rowIndex
4246
+ * @param {Array} focusableColIndices
4247
+ * @param {Element=} content
4248
+ */
4249
+ Grid.prototype._findNextFocusableCell = function(e, colIndex, rowIndex, focusableColIndices, content) {
4250
+ let startIdx = focusableColIndices.indexOf(colIndex);
4251
+
4252
+ // Calculate starting row and column index
4253
+ if(!isValidTarget(e.target, this._hiddenInput) || startIdx < 0) {
4254
+ rowIndex = 0;
4255
+ startIdx = 0;
4256
+ } else if(isValidInput(content) && startIdx >= 0) {
4257
+ startIdx++;
4258
+ }
4259
+ let grid = this._grid;
4260
+ let section = grid.getSection("content");
4261
+ let viewInfo = grid.getVerticalViewInfo();
4262
+ let bottomRowIndex = viewInfo.bottomRowIndex;
4263
+ let rowCount = this.getRowCount();
4264
+ for(let r = rowIndex; r < rowCount; r++) {
4265
+ for(let i = startIdx; i < focusableColIndices.length; i++) {
4266
+ let c = focusableColIndices[i];
4267
+ if(r > bottomRowIndex) {
4268
+ this._requestScroll(e, c, r, focusableColIndices, section);
4269
+ return;
4270
+ } else {
4271
+ let cell = section.getCell(c, r);
4272
+ if(focusCell(cell)) {
4273
+ e.preventDefault();
4274
+ return;
4275
+ }
4276
+ }
4277
+ }
4278
+ startIdx = 0;
4279
+ }
4280
+ };
4281
+ /** @private
4282
+ * @param {Object} e
4283
+ * @param {number} colIndex
4284
+ * @param {number} rowIndex
4285
+ * @param {Array} focusableColIndices
4286
+ * @param {Element=} content
4287
+ */
4288
+ Grid.prototype._findPrevFocusableCell = function(e, colIndex, rowIndex, focusableColIndices, content) {
4289
+ let startIdx = focusableColIndices.indexOf(colIndex);
4290
+ let len = focusableColIndices.length;
4291
+
4292
+ // Calculate starting row and column index
4293
+ if(!isValidTarget(e.target, this._hiddenInput) || startIdx < 0) {
4294
+ rowIndex = 0;
4295
+ startIdx = 0;
4296
+ } else if(isValidInput(content) && startIdx >= 0) {
4297
+ startIdx--;
4298
+ }
4299
+
4300
+ let grid = this._grid;
4301
+ let section = grid.getSection("content");
4302
+ let viewInfo = grid.getVerticalViewInfo();
4303
+ let topRowIndex = viewInfo.topRowIndex;
4304
+ for(let r = rowIndex; r >= 0; r--) {
4305
+ for(let i = startIdx; i >= 0; i--) {
4306
+ let c = focusableColIndices[i];
4307
+ if(r < topRowIndex) {
4308
+ this._requestScroll(e, c, r, focusableColIndices, section);
4309
+ return;
4310
+ } else {
4311
+ let cell = section.getCell(c, r);
4312
+ if(focusCell(cell)) {
4313
+ e.preventDefault();
4314
+ return;
4315
+ }
4316
+ }
4317
+ }
4318
+ startIdx = len - 1;
4319
+ }
4320
+ };
4321
+
4322
+ /** @private
4323
+ * @param {Object} e
4324
+ */
4325
+ Grid.prototype._onKeyDown = function(e) {
4326
+ if (e.keyCode !== 9 || e.ctrlKey || e.altKey || e.metaKey) {
4327
+ return;
4328
+ }
4329
+
4330
+ // Find next focusable cell
4331
+ let colDefs = this.getColumnDefinitions();
4332
+ let colCount = colDefs.length;
4333
+
4334
+ let focusableColIndices = [];
4335
+ for(let c = 0; c < colCount; c++) {
4336
+ if(colDefs[c].isFocusable()) {
4337
+ focusableColIndices.push(c);
4338
+ }
4339
+ }
4340
+
4341
+ if(!focusableColIndices.length) {
4342
+ return;
4343
+ }
4344
+
4345
+ this._scrolledRow = -1; // Reset the scroll loop protector
4346
+ let pos = this.getRelativePosition(e);
4347
+ let content = pos["cell"] ? pos["cell"].getContent() : null;
4348
+
4349
+ if(e.shiftKey) {
4350
+ this._findPrevFocusableCell(e, pos["colIndex"], pos["rowIndex"], focusableColIndices, content);
4351
+ } else {
4352
+ this._findNextFocusableCell(e, pos["colIndex"], pos["rowIndex"], focusableColIndices, content);
4353
+ }
4354
+ };
4355
+
4135
4356
  export { Grid };
4136
4357
  export default Grid;
@@ -31,7 +31,9 @@ declare namespace InCellEditingPlugin {
31
31
  rowEditorClosed?: ((...params: any[]) => any)|null,
32
32
  autoSuggest?: Element|null,
33
33
  closingOnScroll?: boolean|null,
34
- autoHiding?: boolean|null
34
+ autoHiding?: boolean|null,
35
+ readonly?: boolean|null,
36
+ starterText?: (string|boolean)|null
35
37
  };
36
38
 
37
39
  type Cache = {
@@ -81,6 +83,8 @@ declare class InCellEditingPlugin extends GridPlugin {
81
83
 
82
84
  public disableDblClick(opt_disabled?: boolean|null): void;
83
85
 
86
+ public showStarterText(bool?: boolean|null): void;
87
+
84
88
  public isEditing(): boolean;
85
89
 
86
90
  public getTextBox(columnIndex?: number|null, grid?: any): Element|null;
@@ -99,6 +103,10 @@ declare class InCellEditingPlugin extends GridPlugin {
99
103
 
100
104
  public isColumnEditable(colIndex: number): boolean;
101
105
 
106
+ public enableReadonly(bool: boolean): void;
107
+
108
+ public disableReadonly(bool: boolean): void;
109
+
102
110
  public openRowEditor(rowIndex: number, grid?: any): void;
103
111
 
104
112
  public closeRowEditor(isCommit?: boolean|null): void;
@@ -31,6 +31,8 @@ import { CoralItems } from "../../tr-grid-util/es6/CoralItems.js";
31
31
  * @property {Element=} autoSuggest=null Element of ef-autosuggest or atlas-autosuggest for handled with input cell
32
32
  * @property {boolean=} closingOnScroll=true If disabled, the editor will not be automatically closed when scrolling grid.
33
33
  * @property {boolean=} autoHiding=true If disabled, the editor will not be automatically closed when losing its focus.
34
+ * @property {boolean=} readonly=false If disabled, the editor will not be able to open, this property overwrite `editableContent` and `editableTitle`
35
+ * @property {(string|boolean)=} starterText=false if enable it, it will be show placeholder when no ric in grid, given string to overwrite wording
34
36
  */
35
37
 
36
38
  /** @typedef {Object} InCellEditingPlugin~Cache
@@ -171,6 +173,8 @@ let InCellEditingPlugin = function (options) {
171
173
  t._onMultiSelectionValueChanged = t._onMultiSelectionValueChanged.bind(t);
172
174
  t._onMultiSelectionEditorChanged = t._onMultiSelectionEditorChanged.bind(t);
173
175
  t._onAutoSuggestItemSelected = t._onAutoSuggestItemSelected.bind(t);
176
+ t._firstRendered = t._firstRendered.bind(t);
177
+ t._onGridKeyDown = t._onGridKeyDown.bind(t);
174
178
  t._hosts = [];
175
179
 
176
180
  if(options) {
@@ -341,10 +345,26 @@ InCellEditingPlugin.prototype._closingOnScroll = true;
341
345
  * @private
342
346
  */
343
347
  InCellEditingPlugin.prototype._autoHiding = true;
348
+ /** @type {boolean}
349
+ * @private
350
+ */
351
+ InCellEditingPlugin.prototype._readonly = false;
352
+ /** @type {string}
353
+ * @private
354
+ */
355
+ InCellEditingPlugin.prototype._starterText = "";
356
+ /** @type {Object}
357
+ * @private
358
+ */
359
+ InCellEditingPlugin.prototype._starterTextPopup = null;
344
360
  /** @type {number}
345
361
  * @private
346
362
  */
347
363
  InCellEditingPlugin.prototype._editorTimerId = 0;
364
+ /** @type {Object}
365
+ * @private
366
+ */
367
+ InCellEditingPlugin.prototype._rowSelectionPlugin = null;
348
368
  /** @type {boolean}
349
369
  * @private
350
370
  */
@@ -369,6 +389,11 @@ InCellEditingPlugin._styles = prettifyCss([
369
389
  ],
370
390
  ":host .cell.editing", [
371
391
  "z-index: 2;"
392
+ ],
393
+ ":host .starter-text", [
394
+ "padding: var(--grid-cell-padding,0 8px 0 8px);",
395
+ "pointer-events: none;",
396
+ "line-height: 28px" // WARNING: hardcode, it can be calculate with row height
372
397
  ]
373
398
  ]);
374
399
 
@@ -551,6 +576,9 @@ InCellEditingPlugin.prototype.initialize = function (host, options) {
551
576
  }
552
577
 
553
578
  host.listen("columnAdded", this._onColumnAdded);
579
+ host.listen("firstRendered", this._firstRendered);
580
+ host.listen("keydown", this._onGridKeyDown);
581
+
554
582
  host.getVScrollbar().listen("scroll", this._onScroll);
555
583
  host.getHScrollbar().listen("scroll", this._onScroll);
556
584
 
@@ -604,6 +632,14 @@ InCellEditingPlugin.prototype._afterInit = function () {
604
632
  this._elfVersion = ElfUtil.getElfVersion();
605
633
  };
606
634
 
635
+ /** @private
636
+ */
637
+ InCellEditingPlugin.prototype._firstRendered = function () {
638
+ if(!this._readonly && this._starterText) {
639
+ this.showStarterText();
640
+ }
641
+ };
642
+
607
643
  /** @public
608
644
  * @param {Object=} options
609
645
  */
@@ -679,6 +715,16 @@ InCellEditingPlugin.prototype.config = function(options) {
679
715
  if(pluginOption["autoHiding"] == false) {
680
716
  t._autoHiding = false;
681
717
  }
718
+ if(pluginOption["readonly"] == true) {
719
+ t._readonly = true;
720
+ }
721
+ if(pluginOption["starterText"] !== null) {
722
+ if(typeof pluginOption["starterText"] === "string") {
723
+ t._starterText = pluginOption["starterText"];
724
+ } else {
725
+ t._starterText = pluginOption["starterText"] ? "Type to add" : false;
726
+ }
727
+ }
682
728
 
683
729
  // event callback
684
730
  t.addListener(pluginOption, "preEditorOpening");
@@ -773,6 +819,15 @@ InCellEditingPlugin.prototype.getConfigObject = function (out_obj) {
773
819
  dirty |= _setBooleanOption(extOptions, "uiBlocking", this._uiBlocking, false);
774
820
  dirty |= _setBooleanOption(extOptions, "closingOnScroll", this._closingOnScroll, true);
775
821
  dirty |= _setBooleanOption(extOptions, "autoHiding", this._autoHiding, true);
822
+ dirty |= _setBooleanOption(extOptions, "readonly", this._readonly, false);
823
+
824
+ if(this._starterText) {
825
+ if(this._starterText === "Type to add") {
826
+ extOptions["starterText"] = true;
827
+ } else {
828
+ extOptions["starterText"] = this._starterText;
829
+ }
830
+ }
776
831
 
777
832
  if(this._contentSource !== "textContent") {
778
833
  dirty = 1;
@@ -829,8 +884,14 @@ InCellEditingPlugin.prototype.unload = function (host) {
829
884
  clearTimeout(this._editorTimerId);
830
885
  }
831
886
  window.removeEventListener("scroll", this._onScroll);
887
+ if(this._starterTextPopup) {
888
+ this._starterTextPopup.dispose();
889
+ }
890
+ }
891
+ if(this.isEditing()) {
892
+ this.closeRowEditor(false);
893
+ this.closeCellEditor(false);
832
894
  }
833
-
834
895
  this._dispose();
835
896
  };
836
897
 
@@ -848,6 +909,9 @@ InCellEditingPlugin.prototype.openEditor = function (colIndex, rowIndex, section
848
909
  return;
849
910
  }
850
911
  }
912
+ if(this._readonly) {
913
+ return;
914
+ }
851
915
 
852
916
  let sectionSettings = grid.getSectionSettings(sectionRef || "content");
853
917
  let activePos = this._activePos;
@@ -937,6 +1001,114 @@ InCellEditingPlugin.prototype.disableDblClick = function (opt_disabled) {
937
1001
  }
938
1002
  };
939
1003
 
1004
+ /** @private
1005
+ * @param {Object} e
1006
+ */
1007
+ InCellEditingPlugin.prototype._onGridKeyDown = function (e) {
1008
+ if (e.key.length !== 1) { // Special character
1009
+ return;
1010
+ }
1011
+ if(this._starterText && this._starterTextPopup.isShown()) {
1012
+ let rsp = this._getPlugin("RowSelectionPlugin");
1013
+ if(rsp && rsp.getSelectedRowCount() > 0) {
1014
+ return;
1015
+ }
1016
+ let grid = this._hosts[0];
1017
+ grid.scrollToRow("content", 0, true);
1018
+ if(!this.isEditing()) {
1019
+ let firstEditableCol = this._getFirstEditableColumnIndex();
1020
+ if(firstEditableCol === 0) {
1021
+ return;
1022
+ }
1023
+ let cell = grid.getCell("content", firstEditableCol, 0);
1024
+ this._openEditor(cell, grid);
1025
+ }
1026
+ }
1027
+ };
1028
+
1029
+ /**
1030
+ * @description Show starter text when the grid is no ric
1031
+ * @public
1032
+ * @param {boolean=} bool
1033
+ */
1034
+ InCellEditingPlugin.prototype.showStarterText = function (bool) {
1035
+ setTimeout(this._showStarterText.bind(this, bool), 0); // Need to delay to wait scrollbar or editor closed
1036
+ };
1037
+
1038
+
1039
+ /**
1040
+ * @private
1041
+ * @param {boolean=} bool
1042
+ */
1043
+ InCellEditingPlugin.prototype._showStarterText = function (bool) {
1044
+ if(!this._realTimeGrid || !this._starterText) {
1045
+ return;
1046
+ }
1047
+ let allRics = this._realTimeGrid.getAllRics();
1048
+ if(allRics.length > 0) {
1049
+ return;
1050
+ }
1051
+ let popupElem;
1052
+ if(!this._starterTextPopup) {
1053
+ popupElem = this._starterTextPopup = this._createStaterTextElement();
1054
+ } else {
1055
+ popupElem = this._starterTextPopup;
1056
+ }
1057
+ if(bool === false) {
1058
+ popupElem.hide();
1059
+ return;
1060
+ }
1061
+ let grid = this._hosts[0];
1062
+
1063
+ let editableColIndex = this._getFirstEditableColumnIndex();
1064
+ if(editableColIndex < 0) { // not editable
1065
+ return;
1066
+ }
1067
+ grid.scrollToRow("content", 0, true);
1068
+ let editableCell = grid.getCell("content", editableColIndex, 0);
1069
+ popupElem.attachTo(editableCell.getElement());
1070
+ popupElem.show(bool, grid.getElement().parentElement);
1071
+ };
1072
+
1073
+ /** @private
1074
+ * @return {number}
1075
+ */
1076
+ InCellEditingPlugin.prototype._getFirstEditableColumnIndex = function () {
1077
+ if(this._readonly || !this._editableContent) {
1078
+ return -1;
1079
+ }
1080
+
1081
+ let colCount = this.getColumnCount();
1082
+ for (let i = 0; i < colCount; i++) {
1083
+ let editableCol = this.isColumnEditable(i);
1084
+ if(editableCol) {
1085
+ return i;
1086
+ }
1087
+ }
1088
+ return -1;
1089
+ };
1090
+
1091
+ /** @private
1092
+ * @return {Object}
1093
+ */
1094
+ InCellEditingPlugin.prototype._createStaterTextElement = function () {
1095
+ let container = document.createElement("div");
1096
+ container.textContent = this._starterText;
1097
+ container.className = "starter-text";
1098
+ let popup = new Popup(container, { positioning: "over"});
1099
+ popup.disableAutoHiding(true);
1100
+ popup.disableHideOnScroll(true);
1101
+ return popup;
1102
+ };
1103
+
1104
+ /** @public
1105
+ * @description This is for testing purpose to compare element
1106
+ * @ignore
1107
+ * @return {Object}
1108
+ */
1109
+ InCellEditingPlugin.prototype.getStarterTextelement = function () {
1110
+ return this._starterTextPopup;
1111
+ };
940
1112
  /** Checking Editing is in process.
941
1113
  * @public
942
1114
  * @return {boolean}
@@ -1090,9 +1262,27 @@ InCellEditingPlugin.prototype.enableAutoCommitText = function (opt_enable) {
1090
1262
  * @return {boolean}
1091
1263
  */
1092
1264
  InCellEditingPlugin.prototype.isColumnEditable = function (colIndex) {
1265
+ if(this._readonly) {
1266
+ return false;
1267
+ }
1093
1268
  let val = this._getColumnOption(colIndex, "editableContent");
1094
1269
  return val == null ? this._editableContent : val;
1095
1270
  };
1271
+
1272
+ /**
1273
+ * @public
1274
+ * @param {boolean} bool
1275
+ */
1276
+ InCellEditingPlugin.prototype.enableReadonly = function (bool) {
1277
+ this._readonly = bool !== false;
1278
+ };
1279
+ /**
1280
+ * @public
1281
+ * @param {boolean} bool
1282
+ */
1283
+ InCellEditingPlugin.prototype.disableReadonly = function () {
1284
+ this.enableReadonly(false);
1285
+ };
1096
1286
  /** @public
1097
1287
  * @description Supply an keyboard input. This is for testing purpose.
1098
1288
  * @ignore
@@ -1105,6 +1295,16 @@ InCellEditingPlugin.prototype.supplyKey = function(keyName, context) {
1105
1295
  this._onTextKeyUp(eventObj);
1106
1296
  };
1107
1297
  /** @public
1298
+ * @description Supply an keyboard input. This is for testing purpose.
1299
+ * @ignore
1300
+ * @param {string} keyName
1301
+ * @param {Object=} context
1302
+ */
1303
+ InCellEditingPlugin.prototype.supplyGridKeydown = function(keyName, context) {
1304
+ let eventObj = this._mockKeyboardEvent(keyName, context);
1305
+ this._onGridKeyDown(eventObj);
1306
+ };
1307
+ /** @public
1108
1308
  * @description Supply an double click event. This is for testing purpose.
1109
1309
  * @ignore
1110
1310
  * @param {number} colIndex
@@ -1122,6 +1322,9 @@ InCellEditingPlugin.prototype.mockDoubleClick = function(colIndex, rowIndex, con
1122
1322
  */
1123
1323
  InCellEditingPlugin.prototype._onDoubleClick = function (e, opt_host) {
1124
1324
  let t = this;
1325
+ if(t._readonly) {
1326
+ return;
1327
+ }
1125
1328
  let host = opt_host || t.getRelativeGrid(e);
1126
1329
  if(t.isEditing() || !host) { return; }
1127
1330
 
@@ -1318,6 +1521,7 @@ InCellEditingPlugin.prototype._openEditor = function (e, host, arg) {
1318
1521
 
1319
1522
  // Dispatch an event for user to setup stuff
1320
1523
  t._dispatch("editorOpened", arg); // User may modify the editor
1524
+ t.showStarterText(false);
1321
1525
 
1322
1526
  inputElement.focus();
1323
1527
  if(typeof inputElement.select === "function") {
@@ -1337,7 +1541,7 @@ InCellEditingPlugin.prototype.openRowEditor = function (rowIndex, grid) {
1337
1541
  let t = this;
1338
1542
  grid = grid || t._hosts[0];
1339
1543
  // if open same row we will do nothing
1340
- if(t._getRowIndex(t._activeRowId) === rowIndex || !grid) { return; }
1544
+ if(t._getRowIndex(t._activeRowId) === rowIndex || !grid || t._readonly) { return; }
1341
1545
 
1342
1546
  // close all open editor
1343
1547
  t.closeRowEditor(false, grid);
@@ -1835,6 +2039,7 @@ InCellEditingPlugin.prototype._commitText = function (committed, suggestionDetai
1835
2039
  Dom.removeParent(t._customElement);
1836
2040
 
1837
2041
  let grid = arg["grid"];
2042
+ this.showStarterText();
1838
2043
  if(grid) {
1839
2044
  t._freezeScrolling(grid, false);
1840
2045
  grid.focus();