@refinitiv-ui/efx-grid 6.0.108 → 6.0.109

Sign up to get free protection for your applications and to get access to all the features.
@@ -16915,9 +16915,9 @@ GroupDefinitions.prototype.setGroup = function (groupId, groupDef) {
16915
16915
  if(curDef) { // Replace
16916
16916
  this.removeAllChildren(groupId);
16917
16917
  }
16918
- let parentDef = this._childToParent[groupId];
16919
- if(parentDef) {
16920
- newDef.parentId = parentDef.id;
16918
+ let parentId = this._childToParent[groupId];
16919
+ if(parentId) {
16920
+ newDef.parentId = parentId;
16921
16921
  }
16922
16922
  this._groupMap[groupId] = newDef;
16923
16923
 
@@ -23251,7 +23251,7 @@ Scrollbar.prototype._clearAllPanes = function() {
23251
23251
  */
23252
23252
  Scrollbar.prototype.disableKeyboardInput = function (opt_disabled) {
23253
23253
  if(opt_disabled === false) {
23254
- this._element.setAttribute("tabindex", "0");
23254
+ this._element.setAttribute("tabindex", "-1"); // tabindex makes the element focusable. The negative value exclude it from tab key navigation
23255
23255
  this._element.addEventListener("keydown", this._onKeyDown, false);
23256
23256
  } else {
23257
23257
  this._element.removeAttribute("tabindex");
@@ -35706,6 +35706,9 @@ VirtualizedLayoutGrid._proto = VirtualizedLayoutGrid.prototype;
35706
35706
  /** @event Core#preForcedUpdate
35707
35707
  * @ignore
35708
35708
  */
35709
+ /** @event Core#tabNavigation
35710
+ * @ignore
35711
+ */
35709
35712
  //#endregion Events
35710
35713
 
35711
35714
  /** @private
@@ -35716,6 +35719,44 @@ VirtualizedLayoutGrid._proto = VirtualizedLayoutGrid.prototype;
35716
35719
  let ascNumberSorter = function (a, b) {
35717
35720
  return a - b;
35718
35721
  };
35722
+ /** @private
35723
+ * @return {!Element}
35724
+ */
35725
+ let _createHiddenInput = function () {
35726
+ let hiddenInput = document.createElement("input");
35727
+ let styleObj = hiddenInput.style;
35728
+ styleObj.position = "absolute";
35729
+ styleObj.width = styleObj.height = styleObj.padding = styleObj.border = "0";
35730
+ hiddenInput.value = "0";
35731
+ return hiddenInput;
35732
+ };
35733
+ /** @private
35734
+ * @param {Object} e
35735
+ * @return {boolean}
35736
+ */
35737
+ let _isTabCommand = function (e) {
35738
+ if(e.keyCode === 9) {
35739
+ return !e.ctrlKey && !e.altKey && !e.metaKey;
35740
+ }
35741
+ return false;
35742
+ };
35743
+ /** @private
35744
+ * @param {Element} elem
35745
+ * @return {Element}
35746
+ */
35747
+ let _getActiveElement = function (elem) {
35748
+ if(elem) {
35749
+ if(elem.getRootNode) {
35750
+ let rootNode = elem.getRootNode(); // Get uncomposed root node
35751
+ if(rootNode && rootNode !== elem) { // The root node could be the element itself, if it is not attached to the DOM tree
35752
+ return rootNode.activeElement || null;
35753
+ }
35754
+ } else { // Older browser does not support getRootNode
35755
+ return document.activeElement;
35756
+ }
35757
+ }
35758
+ return null;
35759
+ };
35719
35760
 
35720
35761
  /** @constructor
35721
35762
  * @param {Element=} opt_initializer this can be either element id (string) or DOM element.
@@ -35739,6 +35780,8 @@ let Core = function (opt_initializer) {
35739
35780
  _t._onMouseMove = _t._onMouseMove.bind(_t);
35740
35781
  _t._onRowHightlighted = _t._onRowHightlighted.bind(_t);
35741
35782
  _t._onGridClicked = _t._onGridClicked.bind(_t);
35783
+ _t._onKeyDown = _t._onKeyDown.bind(_t);
35784
+ _t._onKeyUp = _t._onKeyUp.bind(_t);
35742
35785
 
35743
35786
  _t._onWindowResize = _t._onWindowResize.bind(_t);
35744
35787
  _t._onSectionDataChanged = _t._onSectionDataChanged.bind(_t);
@@ -35759,7 +35802,7 @@ let Core = function (opt_initializer) {
35759
35802
  _t._onColInViewChanged = _t._onColInViewChanged.bind(_t);
35760
35803
 
35761
35804
  _t._updateVScrollbar = _t._updateVScrollbar.bind(_t);
35762
- _t._updateColumnBounds = _t._updateColumnBounds.bind(_t);
35805
+ _t.updateColumnBounds = _t.updateColumnBounds.bind(_t);
35763
35806
  _t._dispatchColumnPositionChanged = _t._dispatchColumnPositionChanged.bind(_t);
35764
35807
  _t._dispatchRowPositionChanged = _t._dispatchRowPositionChanged.bind(_t);
35765
35808
  _t._requestScrollbarUpdate = _t._requestScrollbarUpdate.bind(_t);
@@ -35800,7 +35843,7 @@ let Core = function (opt_initializer) {
35800
35843
  // Initialize vertical scrollbar
35801
35844
  _t._vscrollbar = new components_VScrollbar();
35802
35845
  _t._vscrollbar.disable();
35803
- _t._vscrollbar.setParent(this.getParent() || this.getElement());
35846
+ _t._vscrollbar.setParent(_t.getParent() || _t.getElement());
35804
35847
 
35805
35848
  _t._vscrollbar.listen("scroll", _t._onVScroll);
35806
35849
  _t._vscrollbar.listen("layoutChanged", _t._onVScroll);
@@ -35813,25 +35856,28 @@ let Core = function (opt_initializer) {
35813
35856
  // Initialize horizontal scrollbars
35814
35857
  _t._hscrollbar = new components_HScrollbar();
35815
35858
  _t._hscrollbar.disable();
35816
- _t._hscrollbar.setParent(this.getParent() || this.getElement());
35859
+ _t._hscrollbar.setParent(_t.getParent() || _t.getElement());
35817
35860
 
35818
- _t._hscrollbar.listen("scroll", this._onHScroll);
35819
- _t._hscrollbar.listen("layoutChanged", this._onHScroll);
35820
- _t._hscrollbar.listen("activated", this.updateLayout);
35821
- _t._hscrollbar.listen("deactivated", this.updateLayout);
35861
+ _t._hscrollbar.listen("scroll", _t._onHScroll);
35862
+ _t._hscrollbar.listen("layoutChanged", _t._onHScroll);
35863
+ _t._hscrollbar.listen("activated", _t.updateLayout);
35864
+ _t._hscrollbar.listen("deactivated", _t.updateLayout);
35822
35865
 
35823
35866
  // cross-reference scrollbars
35824
35867
  _t._hscrollbar.setOtherScrollbar(_t._vscrollbar);
35825
35868
  _t._vscrollbar.setOtherScrollbar(_t._hscrollbar);
35826
35869
 
35870
+
35871
+ _t._element.addEventListener("keydown", _t._onKeyDown);
35872
+ _t._element.addEventListener("keyup", _t._onKeyUp);
35827
35873
  if (util.isMobile || util.isTouchDevice) {
35828
- _t._element.addEventListener("touchmove", this._onMouseMove, false);
35874
+ _t._element.addEventListener("touchmove", _t._onMouseMove, false);
35829
35875
  } else {
35830
- _t._element.addEventListener("mousemove", this._onMouseMove, false);
35876
+ _t._element.addEventListener("mousemove", _t._onMouseMove, false);
35831
35877
  }
35832
35878
 
35833
35879
  if(util.isSafari){
35834
- _t._element.addEventListener("click", this._onGridClicked);
35880
+ _t._element.addEventListener("click", _t._onGridClicked);
35835
35881
  }
35836
35882
 
35837
35883
  window.addEventListener("resize", _t._onWindowResize, false); // Should be unlistened after destroyed
@@ -35839,10 +35885,17 @@ let Core = function (opt_initializer) {
35839
35885
  _t._colVirtualizer.listen("indexChanged", _t._onColInViewChanged);
35840
35886
  _t._rowHeightConflator = new util_Conflator(_t._onRowHeightChanged, 50);
35841
35887
  _t._vScrollbarConflator = new util_Conflator(_t._updateVScrollbar, 200);
35842
- _t._columnBoundConflator = new util_Conflator(_t._updateColumnBounds, 10);
35888
+ _t._columnBoundConflator = new util_Conflator(_t.updateColumnBounds, 10);
35843
35889
  _t._columnPositionConflator = new util_Conflator(_t._dispatchColumnPositionChanged, 10);
35844
35890
  _t._rowPositionConflator = new util_Conflator(_t._dispatchRowPositionChanged, 10);
35845
35891
 
35892
+ _t._firstHiddenInput = _createHiddenInput();
35893
+ _t._firstHiddenInput.className = "first-input";
35894
+ _t._lastHiddenInput = _createHiddenInput();
35895
+ _t._lastHiddenInput.className = "last-input";
35896
+ _t._element.insertBefore(_t._firstHiddenInput, _t._element.firstChild);
35897
+ _t._element.appendChild(_t._lastHiddenInput);
35898
+
35846
35899
  // Initialize events for external users
35847
35900
  _t._addEvents(
35848
35901
  "sectionAdded",
@@ -35871,7 +35924,8 @@ let Core = function (opt_initializer) {
35871
35924
  "beforeColumnBoundUpdate",
35872
35925
  "beforeBatchOperation",
35873
35926
  "afterBatchOperation",
35874
- "pinningChanged"
35927
+ "pinningChanged",
35928
+ "tabNavigation"
35875
35929
  );
35876
35930
 
35877
35931
  // For debugging in advanced optimization mode
@@ -35880,9 +35934,9 @@ let Core = function (opt_initializer) {
35880
35934
  map = {};
35881
35935
  Core["map"] = map;
35882
35936
  }
35883
- let elem = _t._element;
35884
- elem["_control"] = _t;
35885
- let id = elem.id || elem.name;
35937
+
35938
+ _t._element["_control"] = _t;
35939
+ let id = _t._element.id || _t._element.name;
35886
35940
  if(!id || map[id]) {
35887
35941
  id = "_grid" + Core._runningGridId;
35888
35942
  }
@@ -35890,17 +35944,6 @@ let Core = function (opt_initializer) {
35890
35944
  map[id] = _t;
35891
35945
  Core._runningGridId++;
35892
35946
 
35893
- // init hiddenInput for retrieve copy and cut event
35894
- let hiddenInput = document.createElement("input");
35895
- hiddenInput.style.position = "absolute";
35896
- hiddenInput.style.width = "0";
35897
- hiddenInput.style.height = "0";
35898
- hiddenInput.style.padding = "0";
35899
- hiddenInput.style.border = "0";
35900
- hiddenInput.value = "0";
35901
- _t._hiddenInput = hiddenInput;
35902
- elem.insertBefore(hiddenInput, elem.firstChild);
35903
-
35904
35947
  // Ensure all affected plugins are loaded prior zoom plugin
35905
35948
  // use as entity to trigger updateLayout once zoom is changed
35906
35949
  Object.defineProperty(_t, "zoomFactor", {
@@ -36180,11 +36223,16 @@ Core.prototype._rowRefreshTimer = 0;
36180
36223
  * @private
36181
36224
  */
36182
36225
  Core.prototype._layoutUpdating = false;
36183
- /** A Hidden input that allow to get cut and copy event when user perform cut, copy activities
36184
- * @type {Element}
36226
+ /** A hidden input that allows grid to receive keyboard input and focus
36227
+ * @type {!Element}
36228
+ * @private
36229
+ */
36230
+ Core.prototype._firstHiddenInput;
36231
+ /** A hidden input that allows grid to receive keyboard input and focus
36232
+ * @type {!Element}
36185
36233
  * @private
36186
36234
  */
36187
- Core.prototype._hiddenInput;
36235
+ Core.prototype._lastHiddenInput;
36188
36236
  /** @type {number}
36189
36237
  * @private
36190
36238
  */
@@ -36228,7 +36276,7 @@ Core.prototype._hasPendingRowChange = false;
36228
36276
  * @return {string}
36229
36277
  */
36230
36278
  Core.getVersion = function () {
36231
- return "5.1.108";
36279
+ return "5.1.110";
36232
36280
  };
36233
36281
  /** {@link ElementWrapper#dispose}
36234
36282
  * @override
@@ -36289,11 +36337,12 @@ Core.prototype.dispose = function () {
36289
36337
 
36290
36338
  // Clean Top node
36291
36339
  let elem = this._element;
36292
- if (elem !== null) {
36340
+ if (elem) {
36293
36341
  if (elem["_control"]) {
36294
36342
  delete elem["_control"];
36295
36343
  }
36296
- elem.removeChild(this._hiddenInput);
36344
+ elem.removeChild(this._firstHiddenInput);
36345
+ elem.removeChild(this._lastHiddenInput);
36297
36346
  }
36298
36347
  this._dispose();
36299
36348
 
@@ -37458,7 +37507,7 @@ Core.prototype._moveColumn = function (fromCol, destCol) {
37458
37507
  this._colVirtualizer.update();
37459
37508
  }
37460
37509
  }
37461
- this._updateColumnBounds();
37510
+ this.updateColumnBounds();
37462
37511
  this._updateColumnSeparators();
37463
37512
  return true;
37464
37513
  };
@@ -39691,15 +39740,14 @@ Core.prototype.reserveRightSpace = function (size) {
39691
39740
  return false;
39692
39741
  };
39693
39742
 
39694
- /** Get hidden input in grid <br>
39695
- * this input for make grid can copy <br>
39696
- * normal user should not touch it <br>
39697
- * but sometime grid extension will have to use this element
39743
+ /** Get the hidden input. This input allows grid to receive keyboard input
39698
39744
  * @public
39699
- * @return {Element}
39745
+ * @ignore
39746
+ * @param {boolean} firstInput
39747
+ * @return {!Element}
39700
39748
  */
39701
- Core.prototype.getHiddenInput = function () {
39702
- return this._hiddenInput;
39749
+ Core.prototype.getHiddenInput = function (firstInput) {
39750
+ return firstInput ? this._firstHiddenInput : this._lastHiddenInput;
39703
39751
  };
39704
39752
 
39705
39753
  /** Focus grid element without bringing grid into window's view. This is useful when grid is very wide or tall, since window can be scrolled to focused element by default in some browsers.
@@ -39708,8 +39756,8 @@ Core.prototype.getHiddenInput = function () {
39708
39756
  * @see {@link http://help.dottoro.com/ljqmdirr.php}
39709
39757
  */
39710
39758
  Core.prototype.focus = function () {
39711
- let elem = this._hiddenInput;
39712
- let activeElem = document.activeElement;
39759
+ let elem = this._lastHiddenInput;
39760
+ let activeElem = _getActiveElement(elem);
39713
39761
  if(elem && elem !== activeElem) {
39714
39762
  let x = window.pageXOffset;
39715
39763
  let y = window.pageYOffset;
@@ -39840,7 +39888,7 @@ Core.prototype.selectColumn = function (colIndex, selected) {
39840
39888
  for (let i = this._settings.length; --i >= 0; ) {
39841
39889
  this._settings[i].getSection().selectColumn(colIndex, selected);
39842
39890
  }
39843
- this._updateColumnBounds();
39891
+ this.updateColumnBounds();
39844
39892
  };
39845
39893
  /** @public
39846
39894
  * @param {number} colIndex
@@ -39854,9 +39902,9 @@ Core.prototype.isSelectedColumn = function (colIndex) {
39854
39902
  return false;
39855
39903
  };
39856
39904
 
39857
- /** @private
39905
+ /** @public
39858
39906
  */
39859
- Core.prototype._updateColumnBounds = function () {
39907
+ Core.prototype.updateColumnBounds = function () {
39860
39908
  if(this._columnBoundConflator.conflate()) {
39861
39909
  return;
39862
39910
  }
@@ -40343,14 +40391,14 @@ Core.prototype._newSection = function (opt_type, sectionName) {
40343
40391
  Core.prototype._putToLast = function(section) {
40344
40392
  let sectionCount = this._settings.length;
40345
40393
  if (sectionCount === 0) {
40346
- section.setParent(this._element, true);
40394
+ section.insertBefore(this._lastHiddenInput);
40347
40395
  } else {
40348
40396
  let lastGrid = this.getLastSection();
40349
40397
  let nextSibling = lastGrid.getElement().nextSibling;
40350
40398
  if (nextSibling !== null) {
40351
40399
  section.insertBefore(nextSibling);
40352
40400
  } else {
40353
- section.setParent(this._element);
40401
+ section.insertBefore(this._lastHiddenInput);
40354
40402
  }
40355
40403
  }
40356
40404
  };
@@ -40736,7 +40784,7 @@ Core.prototype._onVScroll = function (e) {
40736
40784
  Core.prototype._onHScroll = function (e) {
40737
40785
  let scrollVal = this._hscrollbar.getScrollLeft();
40738
40786
  this._colVirtualizer.setViewOffset(scrollVal); // Trigger virtualization event
40739
- this._updateColumnBounds();
40787
+ this.updateColumnBounds();
40740
40788
  this._dispatchColumnPositionChanged();
40741
40789
  };
40742
40790
  /** @private
@@ -41035,14 +41083,66 @@ Core.prototype._onMouseMove = function () {
41035
41083
  };
41036
41084
  /** @private */
41037
41085
  Core.prototype._onGridClicked = function () {
41038
- // research for dragging
41039
41086
  let selection = window.getSelection();
41040
- if(selection.toString()){
41087
+ if(!selection.toString()){
41088
+ if(!this._element.contains(_getActiveElement(this._element))){
41089
+ this.focus();
41090
+ }
41091
+ }
41092
+ };
41093
+
41094
+ /** @private
41095
+ * @param {Object} e
41096
+ */
41097
+ Core.prototype._onKeyDown = function (e) {
41098
+ if(!_isTabCommand(e)) {
41041
41099
  return;
41042
41100
  }
41043
- let activeElem = document.activeElement;
41044
- if(!this._element.contains(activeElem)){
41045
- this.focus();
41101
+ let activeElement = _getActiveElement(this._element);
41102
+ let onTheEdge = false;
41103
+ if(this._firstHiddenInput === activeElement) {
41104
+ onTheEdge = -1;
41105
+ } else if(this._lastHiddenInput === activeElement) {
41106
+ onTheEdge = 1;
41107
+ }
41108
+
41109
+ this._dispatch("tabNavigation", {
41110
+ "activeElement": activeElement,
41111
+ "firstHiddenInput": this._firstHiddenInput,
41112
+ "lastHiddenInput": this._lastHiddenInput,
41113
+ "onTheEdge": onTheEdge,
41114
+ "shiftKey": e.shiftKey,
41115
+ "event": e
41116
+ });
41117
+
41118
+ if(onTheEdge && !e.defaultPrevented) {
41119
+ if(onTheEdge > 0 && e.shiftKey) {
41120
+ this._firstHiddenInput.focus(); // jump to the top
41121
+ e.preventDefault();
41122
+ } else if(onTheEdge < 0 && !e.shiftKey) {
41123
+ this._lastHiddenInput.focus(); // skip to the end
41124
+ e.preventDefault();
41125
+ }
41126
+ }
41127
+ };
41128
+ /** @private
41129
+ * @param {Object} e
41130
+ */
41131
+ Core.prototype._onKeyUp = function (e) {
41132
+ if(!_isTabCommand(e)) {
41133
+ return;
41134
+ }
41135
+ var activeElem = _getActiveElement(this._element);
41136
+ if(e.shiftKey) {
41137
+ if(activeElem === this._lastHiddenInput) {
41138
+ this._firstHiddenInput.focus();
41139
+ e.preventDefault();
41140
+ }
41141
+ } else {
41142
+ if(activeElem === this._firstHiddenInput) {
41143
+ this._lastHiddenInput.focus();
41144
+ e.preventDefault();
41145
+ }
41046
41146
  }
41047
41147
  };
41048
41148
 
@@ -41225,6 +41325,7 @@ Core.prototype._onSectionCountChanged = function (opt_suppressLayout) {
41225
41325
 
41226
41326
  // Reinsert sections
41227
41327
  this._vscrollbar.setScrollContent(this, this._getAllSections(), this._startVScrollbarIndex);
41328
+ this._element.appendChild(this._lastHiddenInput); // Ensure that the hidden input is always at the last position
41228
41329
 
41229
41330
  if(!opt_suppressLayout) {
41230
41331
  this._updateScrollbarHeight(true, true);
@@ -41242,7 +41343,7 @@ Core.prototype._onColumnCountChanged = function () {
41242
41343
  let pinnedLeft = this._countPinnedLeftColumns();
41243
41344
  let pinnedRight = this._countPinnedRightColumns();
41244
41345
 
41245
- this._updateColumnBounds();
41346
+ this.updateColumnBounds();
41246
41347
  this._updateColumnSeparators();
41247
41348
 
41248
41349
  if (this._hScrollbarEnabled && pinnedLeft + pinnedRight < this.getColumnCount()) {
@@ -41538,7 +41639,7 @@ Core.prototype._syncLayoutToColumns = function (from, to, opt_forceDispatching)
41538
41639
  // TODO: Check if "to" should be greater than or equal to first pinnied right index
41539
41640
  let paneChanged = forceUpdate || (from < this.getHScrollStartIndex()) || (to > this.getFirstPinnedRightIndex());
41540
41641
  this._updateScrollbarWidth(paneChanged, true /* contentChanged */);
41541
- this._updateColumnBounds();
41642
+ this.updateColumnBounds();
41542
41643
  this._updateColumnSeparators();
41543
41644
  this._dispatchColumnPositionChanged();
41544
41645
 
@@ -44728,7 +44829,7 @@ let Grid = function(placeholder, config) {
44728
44829
  t._snapshotFillerDataChanged = t._snapshotFillerDataChanged.bind(t);
44729
44830
  t._onPollingInterval = t._onPollingInterval.bind(t);
44730
44831
 
44731
- t._onKeyDown = t._onKeyDown.bind(t);
44832
+ t._onTabNavigation = t._onTabNavigation.bind(t);
44732
44833
  t._requestScroll = t._requestScroll.bind(t);
44733
44834
  t._onVScroll = t._onVScroll.bind(t);
44734
44835
  t._selfScrollToRow = t._selfScrollToRow.bind(t);
@@ -44811,10 +44912,9 @@ let Grid = function(placeholder, config) {
44811
44912
  t._grid.listen("postSectionDataBinding", t._onPostSectionDataBinding);
44812
44913
  t._grid.listen("firstRendered", t._dispatch.bind(t, "firstRendered"));
44813
44914
  t._grid.listen("afterContentBinding", t._dispatch.bind(t, "afterContentBinding"));
44814
- t._grid.listen("keydown", t._onKeyDown);
44915
+ t._grid.listen("tabNavigation", t._onTabNavigation);
44815
44916
 
44816
44917
  t._grid.getVScrollbar().listen("scroll", t._onVScroll);
44817
- t._hiddenInput = t._grid.getHiddenInput();
44818
44918
 
44819
44919
  t._grid.enableRowHighlighting(true);
44820
44920
 
@@ -45041,8 +45141,10 @@ Grid.prototype.dispose = function() {
45041
45141
  this._subs = null;
45042
45142
  }
45043
45143
 
45044
- if(this._focusingArgs && this._focusingArgs.id) {
45045
- clearTimeout(this._focusingArgs.id);
45144
+ if(this._focusingArgs) {
45145
+ if(this._focusingArgs.id) {
45146
+ clearTimeout(this._focusingArgs.id);
45147
+ }
45046
45148
  this._focusingArgs = null;
45047
45149
  }
45048
45150
  };
@@ -48530,16 +48632,11 @@ Grid.prototype.getVScrollView = function () {
48530
48632
  * @param {Element} el
48531
48633
  * @return {boolean}
48532
48634
  */
48533
- function isValidInput(el) {
48534
- return el && el.tagName !== "SPAN" && !el.disabled;
48535
- }
48536
- /** @private
48537
- * @param {Element} el
48538
- * @param {Element} hiddenInput
48539
- * @return {boolean}
48540
- */
48541
- function isValidTarget(el, hiddenInput) {
48542
- return !el.classList.contains("valigner") && el !== hiddenInput;
48635
+ function isFocusableContent(el) {
48636
+ if(el) {
48637
+ return (el.tagName !== "SPAN" && !el.disabled);
48638
+ }
48639
+ return false;
48543
48640
  }
48544
48641
  /** @private
48545
48642
  * @param {Object} cell
@@ -48548,7 +48645,7 @@ function isValidTarget(el, hiddenInput) {
48548
48645
  function focusCell(cell) {
48549
48646
  if(cell) {
48550
48647
  let cellContent = cell.getContent();
48551
- if(cellContent && isValidInput(cellContent)) {
48648
+ if(cellContent && isFocusableContent(cellContent)) {
48552
48649
  cellContent.focus();
48553
48650
  return true;
48554
48651
  }
@@ -48559,18 +48656,15 @@ function focusCell(cell) {
48559
48656
  */
48560
48657
  Grid.prototype._onVScroll = function() {
48561
48658
  let args = this._focusingArgs;
48562
- if(!args) { return; }
48563
-
48564
- this._focusingArgs = null;
48565
- let event = args.event;
48566
- let cell = args.section.getCell(args.colIndex, args.rowIndex);
48567
- if(focusCell(cell)) {
48568
- event.preventDefault();
48569
- } else {
48570
- if(event.shiftKey) {
48571
- this._findPrevFocusableCell(event, args.colIndex, args.rowIndex, args.focusableColIndices);
48572
- } else {
48573
- this._findNextFocusableCell(event, args.colIndex, args.rowIndex, args.focusableColIndices);
48659
+ if(args) {
48660
+ this._focusingArgs = null;
48661
+ let cell = this._grid.getCell("content", args.colIndex, args.rowIndex);
48662
+ if(!focusCell(cell)) {
48663
+ if(args.shiftKey) {
48664
+ this._focusPrevCellContent(args);
48665
+ } else {
48666
+ this._focusNextCellContent(args);
48667
+ }
48574
48668
  }
48575
48669
  }
48576
48670
  };
@@ -48578,89 +48672,108 @@ Grid.prototype._onVScroll = function() {
48578
48672
  */
48579
48673
  Grid.prototype._selfScrollToRow = function() {
48580
48674
  let args = this._focusingArgs;
48581
- if(!args) { return; }
48582
- args.id = 0;
48583
- this.scrollToRow(args.rowIndex);
48675
+ if(args) {
48676
+ args.id = 0;
48677
+ this.scrollToRow(args.rowIndex);
48678
+ }
48584
48679
  };
48585
48680
  /** @private
48586
- * @param {Object} e
48681
+ * @param {Object} args
48587
48682
  * @param {number} colIndex
48588
48683
  * @param {number} rowIndex
48589
- * @param {Array} focusableColIndices
48590
- * @param {Object} section
48591
48684
  */
48592
- Grid.prototype._requestScroll = function(e, colIndex, rowIndex, focusableColIndices, section) {
48593
- let args = this._focusingArgs;
48594
- if(args) { return; }
48595
- if(this._scrolledRow === rowIndex) { return; } // Avoid infinite loop
48596
-
48597
- this._scrolledRow = rowIndex;
48598
- args = this._focusingArgs = {
48599
- event: e,
48600
- colIndex: colIndex,
48601
- rowIndex: rowIndex,
48602
- focusableColIndices: focusableColIndices,
48603
- section: section
48604
- };
48685
+ Grid.prototype._requestScroll = function(args, colIndex, rowIndex) {
48686
+ if(this._focusingArgs || this._scrolledRow === args.rowIndex) {
48687
+ return; // Avoid infinite loop
48688
+ }
48605
48689
 
48690
+ this._scrolledRow = args.rowIndex;
48691
+ this._focusingArgs = args;
48692
+ args.colIndex = colIndex;
48693
+ args.rowIndex = rowIndex;
48694
+ args.event = null; // The event is invalid after the scroll
48606
48695
  args.id = setTimeout(this._selfScrollToRow); // Avoid event loop protection
48607
48696
  };
48608
48697
  /** @private
48609
- * @param {Object} e
48610
- * @param {number} colIndex
48611
- * @param {number} rowIndex
48612
- * @param {Array} focusableColIndices
48613
- * @param {Element=} content
48698
+ * @param {Object} args
48614
48699
  */
48615
- Grid.prototype._findNextFocusableCell = function(e, colIndex, rowIndex, focusableColIndices, content) {
48616
- let startIdx = focusableColIndices.indexOf(colIndex);
48700
+ Grid.prototype._focusNextCellContent = function(args) {
48701
+ let colIndex = args.colIndex;
48702
+ let rowIndex = args.rowIndex;
48703
+ if(rowIndex < 0 || rowIndex == null) {
48704
+ return;
48705
+ }
48617
48706
 
48618
- // Calculate starting row and column index
48619
- if(!isValidTarget(e.target, this._hiddenInput) || startIdx < 0) {
48620
- rowIndex = 0;
48621
- startIdx = 0;
48622
- } else if(isValidInput(content) && startIdx >= 0) {
48707
+ let focusableColIndices = args.focusableColIndices;
48708
+ let len = focusableColIndices.length;
48709
+ let startIdx = 0;
48710
+ let i;
48711
+ if(colIndex >= 0) {
48712
+ for(i = 1; i < len; i++) {
48713
+ if(colIndex < focusableColIndices[i]) {
48714
+ break;
48715
+ }
48716
+ startIdx = i;
48717
+ }
48718
+ }
48719
+ // If the current focus is on a valid content, starts on the next cell
48720
+ if(args.event && args.validContent) {
48623
48721
  startIdx++;
48624
48722
  }
48723
+
48625
48724
  let grid = this._grid;
48626
48725
  let section = grid.getSection("content");
48627
48726
  let viewInfo = grid.getVerticalViewInfo();
48628
48727
  let bottomRowIndex = viewInfo.bottomRowIndex;
48629
48728
  let rowCount = this.getRowCount();
48630
48729
  for(let r = rowIndex; r < rowCount; r++) {
48631
- for(let i = startIdx; i < focusableColIndices.length; i++) {
48730
+ for(i = startIdx; i < len; i++) {
48632
48731
  let c = focusableColIndices[i];
48633
48732
  if(r > bottomRowIndex) {
48634
- this._requestScroll(e, c, r, focusableColIndices, section);
48733
+ this._requestScroll(args, c, r);
48635
48734
  return;
48636
48735
  } else {
48637
48736
  let cell = section.getCell(c, r);
48638
48737
  if(focusCell(cell)) {
48639
- e.preventDefault();
48738
+ if(args.event) {
48739
+ args.event.preventDefault();
48740
+ }
48640
48741
  return;
48641
48742
  }
48642
48743
  }
48643
48744
  }
48644
48745
  startIdx = 0;
48645
48746
  }
48747
+
48748
+ if(args.validContent) { // The current focus on the last focusable content
48749
+ this._grid.getHiddenInput().focus();
48750
+ }
48646
48751
  };
48647
48752
  /** @private
48648
- * @param {Object} e
48649
- * @param {number} colIndex
48650
- * @param {number} rowIndex
48651
- * @param {Array} focusableColIndices
48652
- * @param {Element=} content
48753
+ * @param {Object} args
48653
48754
  */
48654
- Grid.prototype._findPrevFocusableCell = function(e, colIndex, rowIndex, focusableColIndices, content) {
48655
- let startIdx = focusableColIndices.indexOf(colIndex);
48656
- let len = focusableColIndices.length;
48755
+ Grid.prototype._focusPrevCellContent = function(args) {
48756
+ let colIndex = args.colIndex;
48757
+ let rowIndex = args.rowIndex;
48758
+ if(rowIndex < 0 || rowIndex == null) {
48759
+ return;
48760
+ }
48657
48761
 
48658
- // Calculate starting row and column index
48659
- if(!isValidTarget(e.target, this._hiddenInput) || startIdx < 0) {
48660
- rowIndex = 0;
48661
- startIdx = 0;
48662
- } else if(isValidInput(content) && startIdx >= 0) {
48663
- startIdx--;
48762
+ let focusableColIndices = args.focusableColIndices;
48763
+ let len = focusableColIndices.length;
48764
+ let startIdx = len - 1;
48765
+ let i;
48766
+ if(colIndex >= 0) {
48767
+ for(i = len - 1; --i >= 0;) {
48768
+ if(colIndex > focusableColIndices[i]) {
48769
+ break;
48770
+ }
48771
+ startIdx = i;
48772
+ }
48773
+ }
48774
+ // If the current focus is on a valid content, starts on the next cell
48775
+ if(args.event && args.validContent) {
48776
+ --startIdx;
48664
48777
  }
48665
48778
 
48666
48779
  let grid = this._grid;
@@ -48668,32 +48781,33 @@ Grid.prototype._findPrevFocusableCell = function(e, colIndex, rowIndex, focusabl
48668
48781
  let viewInfo = grid.getVerticalViewInfo();
48669
48782
  let topRowIndex = viewInfo.topRowIndex;
48670
48783
  for(let r = rowIndex; r >= 0; r--) {
48671
- for(let i = startIdx; i >= 0; i--) {
48784
+ for(i = startIdx; i >= 0; i--) {
48672
48785
  let c = focusableColIndices[i];
48673
48786
  if(r < topRowIndex) {
48674
- this._requestScroll(e, c, r, focusableColIndices, section);
48787
+ this._requestScroll(args, c, r);
48675
48788
  return;
48676
48789
  } else {
48677
48790
  let cell = section.getCell(c, r);
48678
48791
  if(focusCell(cell)) {
48679
- e.preventDefault();
48792
+ if(args.event) {
48793
+ args.event.preventDefault();
48794
+ }
48680
48795
  return;
48681
48796
  }
48682
48797
  }
48683
48798
  }
48684
48799
  startIdx = len - 1;
48685
48800
  }
48801
+
48802
+ if(args.validContent) { // The current focus on the last focusable content
48803
+ this._grid.getHiddenInput(true).focus();
48804
+ }
48686
48805
  };
48687
48806
 
48688
48807
  /** @private
48689
48808
  * @param {Object} e
48690
48809
  */
48691
- Grid.prototype._onKeyDown = function(e) {
48692
- if (e.keyCode !== 9 || e.ctrlKey || e.altKey || e.metaKey) {
48693
- return;
48694
- }
48695
-
48696
- // Find next focusable cell
48810
+ Grid.prototype._onTabNavigation = function(e) {
48697
48811
  let colDefs = this.getColumnDefinitions();
48698
48812
  let colCount = colDefs.length;
48699
48813
 
@@ -48709,13 +48823,36 @@ Grid.prototype._onKeyDown = function(e) {
48709
48823
  }
48710
48824
 
48711
48825
  this._scrolledRow = -1; // Reset the scroll loop protector
48712
- let pos = this.getRelativePosition(e);
48713
- let content = pos["cell"] ? pos["cell"].getContent() : null;
48826
+ let keyEvt = e.event;
48827
+ let pos = this.getRelativePosition(keyEvt);
48828
+ let validContent = true;
48829
+ let activeElement = e.activeElement;
48830
+ if(activeElement) {
48831
+ validContent = !activeElement.classList.contains("valigner");
48832
+ }
48833
+
48834
+ if(validContent) {
48835
+ let content = pos["cell"] ? pos["cell"].getContent() : null;
48836
+ validContent = isFocusableContent(content);
48837
+ }
48838
+ let startingRowIndex = pos["rowIndex"];
48839
+ if(e.onTheEdge) {
48840
+ let viewInfo = this._grid.getVScrollView();
48841
+ startingRowIndex = keyEvt.shiftKey ? viewInfo.lastFullRow : viewInfo.firstFullRow;
48842
+ }
48843
+ let args = {
48844
+ event: keyEvt,
48845
+ shiftKey: keyEvt.shiftKey,
48846
+ colIndex: pos["colIndex"],
48847
+ rowIndex: startingRowIndex,
48848
+ focusableColIndices: focusableColIndices,
48849
+ validContent: validContent
48850
+ };
48714
48851
 
48715
- if(e.shiftKey) {
48716
- this._findPrevFocusableCell(e, pos["colIndex"], pos["rowIndex"], focusableColIndices, content);
48852
+ if(keyEvt.shiftKey) {
48853
+ this._focusPrevCellContent(args);
48717
48854
  } else {
48718
- this._findNextFocusableCell(e, pos["colIndex"], pos["rowIndex"], focusableColIndices, content);
48855
+ this._focusNextCellContent(args);
48719
48856
  }
48720
48857
  };
48721
48858