@syncfusion/ej2-treegrid 31.2.15 → 32.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/dist/ej2-treegrid.min.js +2 -2
  2. package/dist/ej2-treegrid.umd.min.js +2 -2
  3. package/dist/ej2-treegrid.umd.min.js.map +1 -1
  4. package/dist/es6/ej2-treegrid.es2015.js +1056 -321
  5. package/dist/es6/ej2-treegrid.es2015.js.map +1 -1
  6. package/dist/es6/ej2-treegrid.es5.js +1062 -324
  7. package/dist/es6/ej2-treegrid.es5.js.map +1 -1
  8. package/dist/global/ej2-treegrid.min.js +2 -2
  9. package/dist/global/ej2-treegrid.min.js.map +1 -1
  10. package/dist/global/index.d.ts +1 -1
  11. package/package.json +5 -5
  12. package/src/treegrid/actions/batch-edit.js +21 -3
  13. package/src/treegrid/actions/context-menu.d.ts +1 -0
  14. package/src/treegrid/actions/context-menu.js +16 -0
  15. package/src/treegrid/actions/edit.d.ts +4 -1
  16. package/src/treegrid/actions/edit.js +56 -3
  17. package/src/treegrid/actions/freeze-column.js +1 -1
  18. package/src/treegrid/actions/rowdragdrop.js +3 -0
  19. package/src/treegrid/actions/selection.d.ts +226 -8
  20. package/src/treegrid/actions/selection.js +759 -288
  21. package/src/treegrid/actions/summary.js +1 -1
  22. package/src/treegrid/actions/virtual-scroll.js +6 -3
  23. package/src/treegrid/base/data.js +2 -1
  24. package/src/treegrid/base/treegrid-model.d.ts +16 -0
  25. package/src/treegrid/base/treegrid.d.ts +50 -4
  26. package/src/treegrid/base/treegrid.js +159 -21
  27. package/src/treegrid/models/column.d.ts +24 -0
  28. package/src/treegrid/models/column.js +12 -0
  29. package/src/treegrid/renderer/virtual-tree-content-render.d.ts +5 -0
  30. package/src/treegrid/renderer/virtual-tree-content-render.js +26 -3
  31. package/styles/bds-lite.scss +17 -18
  32. package/styles/bds.scss +18 -19
  33. package/styles/bootstrap-dark-lite.scss +17 -18
  34. package/styles/bootstrap-dark.scss +18 -19
  35. package/styles/bootstrap-lite.scss +17 -18
  36. package/styles/bootstrap.scss +18 -19
  37. package/styles/bootstrap4-lite.scss +17 -18
  38. package/styles/bootstrap4.scss +18 -19
  39. package/styles/bootstrap5-dark-lite.scss +17 -18
  40. package/styles/bootstrap5-dark.scss +18 -19
  41. package/styles/bootstrap5-lite.scss +18 -18
  42. package/styles/bootstrap5.3-lite.css +81 -1
  43. package/styles/bootstrap5.3-lite.scss +18 -18
  44. package/styles/bootstrap5.3.css +81 -1
  45. package/styles/bootstrap5.3.scss +19 -19
  46. package/styles/bootstrap5.scss +19 -19
  47. package/styles/fabric-dark-lite.scss +18 -18
  48. package/styles/fabric-dark.scss +19 -19
  49. package/styles/fabric-lite.scss +18 -18
  50. package/styles/fabric.scss +19 -19
  51. package/styles/fluent-dark-lite.scss +18 -18
  52. package/styles/fluent-dark.scss +19 -19
  53. package/styles/fluent-lite.scss +18 -18
  54. package/styles/fluent.scss +19 -19
  55. package/styles/fluent2-lite.css +152 -4
  56. package/styles/fluent2-lite.scss +18 -18
  57. package/styles/fluent2.css +152 -4
  58. package/styles/fluent2.scss +19 -19
  59. package/styles/highcontrast-light-lite.scss +18 -18
  60. package/styles/highcontrast-light.scss +19 -19
  61. package/styles/highcontrast-lite.scss +18 -18
  62. package/styles/highcontrast.scss +19 -19
  63. package/styles/material-dark-lite.scss +18 -18
  64. package/styles/material-dark.scss +19 -19
  65. package/styles/material-lite.scss +17 -18
  66. package/styles/material.scss +18 -19
  67. package/styles/material3-dark-lite.css +140 -1
  68. package/styles/material3-dark-lite.scss +18 -18
  69. package/styles/material3-dark.css +140 -1
  70. package/styles/material3-dark.scss +19 -21
  71. package/styles/material3-lite.css +140 -1
  72. package/styles/material3-lite.scss +18 -18
  73. package/styles/material3.css +140 -1
  74. package/styles/material3.scss +19 -21
  75. package/styles/tailwind-dark-lite.scss +18 -18
  76. package/styles/tailwind-dark.scss +19 -19
  77. package/styles/tailwind-lite.scss +18 -18
  78. package/styles/tailwind.scss +19 -19
  79. package/styles/tailwind3-lite.css +119 -1
  80. package/styles/tailwind3-lite.scss +18 -18
  81. package/styles/tailwind3.css +119 -1
  82. package/styles/tailwind3.scss +19 -19
  83. package/styles/treegrid/_all.scss +2 -2
  84. package/styles/treegrid/_bds-definition.scss +2 -0
  85. package/styles/treegrid/_bigger.scss +2 -0
  86. package/styles/treegrid/_bootstrap-dark-definition.scss +2 -0
  87. package/styles/treegrid/_bootstrap-definition.scss +2 -0
  88. package/styles/treegrid/_bootstrap4-definition.scss +2 -0
  89. package/styles/treegrid/_bootstrap5-dark-definition.scss +26 -1
  90. package/styles/treegrid/_bootstrap5-definition.scss +2 -0
  91. package/styles/treegrid/_bootstrap5.3-definition.scss +2 -0
  92. package/styles/treegrid/_fabric-dark-definition.scss +2 -0
  93. package/styles/treegrid/_fabric-definition.scss +2 -0
  94. package/styles/treegrid/_fluent-dark-definition.scss +27 -1
  95. package/styles/treegrid/_fluent-definition.scss +2 -0
  96. package/styles/treegrid/_fluent2-definition.scss +2 -0
  97. package/styles/treegrid/_highcontrast-definition.scss +2 -0
  98. package/styles/treegrid/_highcontrast-light-definition.scss +2 -0
  99. package/styles/treegrid/_layout.scss +5 -2
  100. package/styles/treegrid/_material-dark-definition.scss +2 -0
  101. package/styles/treegrid/_material-definition.scss +2 -0
  102. package/styles/treegrid/_material3-dark-definition.scss +26 -1
  103. package/styles/treegrid/_material3-definition.scss +2 -0
  104. package/styles/treegrid/_tailwind-dark-definition.scss +26 -1
  105. package/styles/treegrid/_tailwind-definition.scss +2 -0
  106. package/styles/treegrid/_tailwind3-definition.scss +2 -0
  107. package/styles/treegrid/_theme-variables.scss +1 -0
  108. package/styles/treegrid/bds.scss +19 -19
  109. package/styles/treegrid/bootstrap-dark.scss +19 -19
  110. package/styles/treegrid/bootstrap.scss +19 -19
  111. package/styles/treegrid/bootstrap4.scss +19 -19
  112. package/styles/treegrid/bootstrap5-dark.scss +19 -19
  113. package/styles/treegrid/bootstrap5.3.css +81 -1
  114. package/styles/treegrid/bootstrap5.3.scss +19 -19
  115. package/styles/treegrid/bootstrap5.scss +19 -19
  116. package/styles/treegrid/fabric-dark.scss +19 -19
  117. package/styles/treegrid/fabric.scss +19 -19
  118. package/styles/treegrid/fluent-dark.scss +19 -19
  119. package/styles/treegrid/fluent.scss +19 -19
  120. package/styles/treegrid/fluent2.css +152 -4
  121. package/styles/treegrid/fluent2.scss +19 -19
  122. package/styles/treegrid/highcontrast-light.scss +19 -19
  123. package/styles/treegrid/highcontrast.scss +19 -19
  124. package/styles/treegrid/icons/_bds.scss +1 -0
  125. package/styles/treegrid/icons/_bootstrap-dark.scss +1 -0
  126. package/styles/treegrid/icons/_bootstrap.scss +1 -0
  127. package/styles/treegrid/icons/_bootstrap4.scss +1 -0
  128. package/styles/treegrid/icons/_bootstrap5-dark.scss +1 -1
  129. package/styles/treegrid/icons/_bootstrap5.3.scss +1 -0
  130. package/styles/treegrid/icons/_bootstrap5.scss +1 -0
  131. package/styles/treegrid/icons/_fabric-dark.scss +1 -0
  132. package/styles/treegrid/icons/_fabric.scss +1 -0
  133. package/styles/treegrid/icons/_fluent-dark.scss +1 -1
  134. package/styles/treegrid/icons/_fluent.scss +1 -0
  135. package/styles/treegrid/icons/_fluent2.scss +1 -0
  136. package/styles/treegrid/icons/_fusionnew.scss +1 -0
  137. package/styles/treegrid/icons/_highcontrast-light.scss +1 -0
  138. package/styles/treegrid/icons/_highcontrast.scss +1 -0
  139. package/styles/treegrid/icons/_material-dark.scss +1 -0
  140. package/styles/treegrid/icons/_material.scss +1 -0
  141. package/styles/treegrid/icons/_material3-dark.scss +1 -1
  142. package/styles/treegrid/icons/_material3.scss +1 -0
  143. package/styles/treegrid/icons/_tailwind-dark.scss +1 -0
  144. package/styles/treegrid/icons/_tailwind.scss +1 -0
  145. package/styles/treegrid/icons/_tailwind3.scss +1 -0
  146. package/styles/treegrid/material-dark.scss +19 -19
  147. package/styles/treegrid/material.scss +19 -19
  148. package/styles/treegrid/material3-dark.css +140 -1
  149. package/styles/treegrid/material3-dark.scss +19 -20
  150. package/styles/treegrid/material3.css +140 -1
  151. package/styles/treegrid/material3.scss +19 -20
  152. package/styles/treegrid/tailwind-dark.scss +19 -19
  153. package/styles/treegrid/tailwind.scss +19 -19
  154. package/styles/treegrid/tailwind3.css +119 -1
  155. package/styles/treegrid/tailwind3.scss +19 -19
@@ -70,6 +70,18 @@ class Column {
70
70
  * @default null
71
71
  */
72
72
  this.filter = {};
73
+ /**
74
+ * Allows treegrid to perform row spanning on the specified column.
75
+ *
76
+ * @default true
77
+ */
78
+ this.enableRowSpan = true;
79
+ /**
80
+ * Allows treegrid to perform column spanning on the specified column.
81
+ *
82
+ * @default true
83
+ */
84
+ this.enableColumnSpan = true;
73
85
  merge(this, options);
74
86
  }
75
87
  /**
@@ -875,27 +887,56 @@ function isHidden(el) {
875
887
  */
876
888
  class Selection {
877
889
  /**
878
- * Constructor for Selection module
890
+ * Creates an instance of Selection.
879
891
  *
880
- * @param {TreeGrid} parent - Tree Grid instance
892
+ * @param {TreeGrid} parent - The TreeGrid instance this selection module is associated with.
881
893
  */
882
894
  constructor(parent) {
895
+ this.headerCheckboxFrameEl = null;
896
+ this.checkboxColIndexCache = -2;
897
+ this.parentSelectionCounters = {};
898
+ this.selectedUidMap = new Map(); // Quick lookup for whether an item is selected
899
+ this.totalSelectableCount = 0;
900
+ this.headerSelectionState = 'uncheck';
901
+ this.checkedItemCount = 0;
902
+ this.visibleUidIndex = {};
883
903
  this.parent = parent;
884
904
  this.selectedItems = [];
885
- this.selectedIndexes = [];
905
+ this.selectedIndexes = []; // Initialize here
886
906
  this.filteredList = [];
887
907
  this.searchingRecords = [];
888
908
  this.addEventListener();
889
909
  }
890
910
  /**
891
- * For internal use only - Get the module name.
911
+ * Gets the module name.
892
912
  *
893
- * @private
894
- * @returns {string} Returns Selection module name
913
+ * @returns {string} The name of the module ('selection').
895
914
  */
896
- getModuleName() {
897
- return 'selection';
915
+ getModuleName() { return 'selection'; }
916
+ /**
917
+ * Builds a map from visible record uniqueID to its visible index.
918
+ * This map is crucial for finding the *current visible index* of a record.
919
+ *
920
+ * @returns {void}
921
+ */
922
+ buildVisibleUidMap() {
923
+ this.visibleUidIndex = {};
924
+ const view = this.parent.grid.currentViewData;
925
+ if (!view) {
926
+ return;
927
+ }
928
+ for (let i = 0, len = view.length; i < len; i++) {
929
+ const rec = view[parseInt(i.toString(), 10)];
930
+ if (rec && rec.uniqueID) {
931
+ this.visibleUidIndex[rec.uniqueID] = i; // Map uid -> visible row index
932
+ }
933
+ }
898
934
  }
935
+ /**
936
+ * Adds required event listeners for selection handling.
937
+ *
938
+ * @returns {void}
939
+ */
899
940
  addEventListener() {
900
941
  this.parent.on('dataBoundArg', this.headerCheckbox, this);
901
942
  this.parent.on('columnCheckbox', this.columnCheckbox, this);
@@ -903,6 +944,11 @@ class Selection {
903
944
  this.parent.grid.on('colgroup-refresh', this.headerCheckbox, this);
904
945
  this.parent.on('checkboxSelection', this.checkboxSelection, this);
905
946
  }
947
+ /**
948
+ * Removes previously added event listeners.
949
+ *
950
+ * @returns {void}
951
+ */
906
952
  removeEventListener() {
907
953
  if (this.parent.isDestroyed) {
908
954
  return;
@@ -914,14 +960,20 @@ class Selection {
914
960
  this.parent.off('updateGridActions', this.updateGridActions);
915
961
  }
916
962
  /**
917
- * To destroy the Selection
963
+ * Destroys the selection module and clears internal caches.
918
964
  *
919
965
  * @returns {void}
920
- * @hidden
921
966
  */
922
967
  destroy() {
968
+ this.resetSelectionCaches();
923
969
  this.removeEventListener();
924
970
  }
971
+ /**
972
+ * Handles checkbox click events from the DOM and dispatches selection logic.
973
+ *
974
+ * @param {Object} args - Event args containing the click target.
975
+ * @returns {void}
976
+ */
925
977
  checkboxSelection(args) {
926
978
  const target = getObject('target', args);
927
979
  const checkWrap = parentsUntil(target, 'e-checkbox-wrapper');
@@ -929,16 +981,23 @@ class Selection {
929
981
  if (checkWrap && checkWrap.querySelectorAll('.e-treecheckselect').length > 0) {
930
982
  checkBox = checkWrap.querySelector('input[type="checkbox"]');
931
983
  const rowIndex = [];
932
- rowIndex.push(target.closest('tr').rowIndex);
984
+ if (this.parent.frozenRows) {
985
+ rowIndex.push(parseInt(target.closest('tr').getAttribute('aria-rowindex'), 10) - 1);
986
+ }
987
+ else {
988
+ rowIndex.push(target.closest('tr').rowIndex);
989
+ }
933
990
  this.selectCheckboxes(rowIndex);
934
- this.triggerChkChangeEvent(checkBox, checkBox.nextElementSibling.classList.contains('e-check'), target.closest('tr'));
991
+ const newCheckState = checkBox.nextElementSibling.classList.contains('e-check');
992
+ this.triggerChkChangeEvent(checkBox, newCheckState, target.closest('tr'));
935
993
  }
936
994
  else if (checkWrap && checkWrap.querySelectorAll('.e-treeselectall').length > 0 && this.parent.autoCheckHierarchy) {
937
- const checkBoxvalue = !checkWrap.querySelector('.e-frame').classList.contains('e-check')
938
- && !checkWrap.querySelector('.e-frame').classList.contains('e-stop');
939
- this.headerSelection(checkBoxvalue);
995
+ const frame = checkWrap.querySelector('.e-frame');
996
+ const currentStateIsUncheck = !frame.classList.contains('e-check') && !frame.classList.contains('e-stop');
997
+ const targetState = currentStateIsUncheck; // If currently uncheck, target state is to check all.
998
+ this.headerSelection(targetState);
940
999
  checkBox = checkWrap.querySelector('input[type="checkbox"]');
941
- this.triggerChkChangeEvent(checkBox, checkBoxvalue, target.closest('tr'));
1000
+ this.triggerChkChangeEvent(checkBox, targetState, target.closest('tr'));
942
1001
  }
943
1002
  if (!isNullOrUndefined(this.parent['parentQuery']) && this.parent.selectionSettings.persistSelection
944
1003
  && this.parent['columnModel'].filter((col) => { return col.type === 'checkbox'; }).length > 0
@@ -949,63 +1008,99 @@ class Selection {
949
1008
  }
950
1009
  }
951
1010
  }
1011
+ /**
1012
+ * Triggers the checkboxChange event with the appropriate arguments.
1013
+ *
1014
+ * @param {HTMLInputElement} checkBox - The checkbox input element that changed.
1015
+ * @param {boolean} checkState - The new checked state.
1016
+ * @param {HTMLTableRowElement} rowElement - The row element where the change occurred.
1017
+ * @returns {void}
1018
+ */
952
1019
  triggerChkChangeEvent(checkBox, checkState, rowElement) {
953
1020
  const data = this.parent.getCurrentViewRecords()[rowElement.rowIndex];
954
- const args = { checked: checkState, target: checkBox, rowElement: rowElement,
1021
+ const args = {
1022
+ checked: checkState, target: checkBox, rowElement: rowElement,
955
1023
  rowData: checkBox.classList.contains('e-treeselectall')
956
- ? this.parent.getCheckedRecords() : data };
1024
+ ? this.parent.getCheckedRecords() : data
1025
+ };
957
1026
  this.parent.trigger(checkboxChange, args);
958
1027
  }
1028
+ /**
1029
+ * Determines the index of the checkbox column in the header.
1030
+ *
1031
+ * @returns {number} The index of the checkbox column, or -1 if not found.
1032
+ */
959
1033
  getCheckboxcolumnIndex() {
1034
+ if (this.checkboxColIndexCache !== -2) {
1035
+ return this.checkboxColIndexCache;
1036
+ }
960
1037
  let mappingUid;
961
- let columnIndex;
1038
+ let columnIndex = -1;
962
1039
  const stackedHeader = 'stackedHeader';
963
1040
  const columnModel = 'columnModel';
964
1041
  const columns = this.parent[`${stackedHeader}`] ? this.parent[`${columnModel}`] : (this.parent.columns);
965
1042
  for (let col = 0; col < columns.length; col++) {
966
1043
  if (columns[parseInt(col.toString(), 10)].showCheckbox) {
967
1044
  mappingUid = columns[parseInt(col.toString(), 10)].uid;
1045
+ break;
968
1046
  }
969
1047
  }
970
- const headerCelllength = this.parent.getHeaderContent().querySelectorAll('.e-headercelldiv').length;
971
- for (let j = 0; j < headerCelllength; j++) {
972
- const headercell = this.parent.getHeaderContent().querySelectorAll('.e-headercelldiv')[parseInt(j.toString(), 10)];
1048
+ const headerDivs = this.parent.getHeaderContent().querySelectorAll('.e-headercelldiv');
1049
+ for (let j = 0; j < headerDivs.length; j++) {
1050
+ const headercell = headerDivs[parseInt(j.toString(), 10)];
973
1051
  if (headercell.getAttribute('data-mappinguid') === mappingUid) {
974
1052
  columnIndex = j;
1053
+ break;
975
1054
  }
976
1055
  }
977
- return columnIndex;
1056
+ this.checkboxColIndexCache = isNullOrUndefined(columnIndex) ? -1 : columnIndex;
1057
+ return this.checkboxColIndexCache;
978
1058
  }
1059
+ /**
1060
+ * Renders and initializes the header checkbox element.
1061
+ *
1062
+ * @returns {void}
1063
+ */
979
1064
  headerCheckbox() {
1065
+ this.buildVisibleUidMap();
1066
+ this.totalSelectableCount = this.countSelectableRecords(this.resolveHeaderSelectionList(true)); // Use all flatData for initial count
980
1067
  this.columnIndex = this.getCheckboxcolumnIndex();
981
- if (this.columnIndex > -1 && this.parent.getHeaderContent().querySelectorAll('.e-treeselectall').length === 0) {
1068
+ if (this.columnIndex > -1) {
982
1069
  const headerElement = this.parent.getHeaderContent().querySelectorAll('.e-headercelldiv')[this.columnIndex];
983
- const value = false;
984
- const rowChkBox = this.parent.createElement('input', { className: 'e-treeselectall', attrs: { 'type': 'checkbox' } });
985
- const checkWrap = createCheckBox(this.parent.createElement, false, { checked: value, label: ' ' });
986
- checkWrap.classList.add('e-hierarchycheckbox');
987
- checkWrap.insertBefore(rowChkBox.cloneNode(), checkWrap.firstChild);
988
- if (!isNullOrUndefined(headerElement)) {
989
- headerElement.insertBefore(checkWrap, headerElement.firstChild);
990
- }
991
- if (this.parent.autoCheckHierarchy) {
992
- this.headerSelection();
1070
+ if (headerElement && headerElement.querySelectorAll('.e-treeselectall').length === 0) {
1071
+ const value = false; // Initial state can be false.
1072
+ const rowChkBox = this.parent.createElement('input', { className: 'e-treeselectall', attrs: { 'type': 'checkbox' } });
1073
+ const checkWrap = createCheckBox(this.parent.createElement, false, { checked: value, label: ' ' });
1074
+ checkWrap.classList.add('e-hierarchycheckbox');
1075
+ checkWrap.insertBefore(rowChkBox.cloneNode(), checkWrap.firstChild);
1076
+ if (!isNullOrUndefined(headerElement)) {
1077
+ headerElement.insertBefore(checkWrap, headerElement.firstChild);
1078
+ }
1079
+ this.headerCheckboxFrameEl = checkWrap.querySelector('.e-frame'); // Assign the frame element
1080
+ if (this.parent.autoCheckHierarchy) {
1081
+ this.headerSelection();
1082
+ } // Update header state based on data
993
1083
  }
994
- }
995
- else if (this.columnIndex > -1 && this.parent.getHeaderContent().querySelectorAll('.e-treeselectall').length > 0) {
996
- const checkWrap = this.parent.getHeaderContent().querySelectorAll('.e-checkbox-wrapper')[0];
997
- const checkBoxvalue = checkWrap.querySelector('.e-frame').classList.contains('e-check');
998
- if (this.parent.autoCheckHierarchy && checkBoxvalue) {
999
- this.headerSelection(checkBoxvalue);
1084
+ else if (headerElement && headerElement.querySelectorAll('.e-treeselectall').length > 0) {
1085
+ this.headerCheckboxFrameEl = headerElement.querySelector('.e-frame');
1086
+ if (this.parent.autoCheckHierarchy) {
1087
+ this.headerSelection();
1088
+ } // Update status based on current selections
1000
1089
  }
1001
1090
  }
1002
1091
  }
1092
+ /**
1093
+ * Renders a checkbox element for a column cell.
1094
+ *
1095
+ * @param {QueryCellInfoEventArgs} args - The QueryCellInfoEventArgs for the cell.
1096
+ * @returns {Element} The rendered checkbox wrapper element.
1097
+ */
1003
1098
  renderColumnCheckbox(args) {
1004
1099
  const rowChkBox = this.parent.createElement('input', { className: 'e-treecheckselect', attrs: { 'type': 'checkbox', 'aria-label': 'checkbox' } });
1005
1100
  const data = args.data;
1006
1101
  args.cell.classList.add('e-treegridcheckbox');
1007
1102
  args.cell.setAttribute('aria-label', 'checkbox');
1008
- const value = (isNullOrUndefined(data.checkboxState) || data.checkboxState === 'uncheck') ? false : true;
1103
+ const value = (data.checkboxState === 'check');
1009
1104
  const checkWrap = createCheckBox(this.parent.createElement, false, { checked: value, label: ' ' });
1010
1105
  checkWrap.classList.add('e-hierarchycheckbox');
1011
1106
  if (this.parent.allowTextWrap) {
@@ -1016,9 +1111,25 @@ class Selection {
1016
1111
  removeClass([checkbox], ['e-check', 'e-stop', 'e-uncheck']);
1017
1112
  checkWrap.querySelector('.e-frame').classList.add('e-stop');
1018
1113
  }
1114
+ else if (data.checkboxState === 'uncheck') {
1115
+ const checkbox = checkWrap.querySelectorAll('.e-frame')[0];
1116
+ removeClass([checkbox], ['e-check', 'e-stop', 'e-uncheck']);
1117
+ checkWrap.querySelector('.e-frame').classList.add('e-uncheck');
1118
+ }
1119
+ else if (data.checkboxState === 'check') {
1120
+ const checkbox = checkWrap.querySelectorAll('.e-frame')[0];
1121
+ removeClass([checkbox], ['e-check', 'e-stop', 'e-uncheck']);
1122
+ checkWrap.querySelector('.e-frame').classList.add('e-check');
1123
+ }
1019
1124
  checkWrap.insertBefore(rowChkBox.cloneNode(), checkWrap.firstChild);
1020
1125
  return checkWrap;
1021
1126
  }
1127
+ /**
1128
+ * Injects the checkbox into a column cell during QueryCellInfo.
1129
+ *
1130
+ * @param {QueryCellInfoEventArgs} container - The cell event args.
1131
+ * @returns {void}
1132
+ */
1022
1133
  columnCheckbox(container) {
1023
1134
  const checkWrap = this.renderColumnCheckbox(container);
1024
1135
  const containerELe = container.cell.querySelector('.e-treecolumn-container');
@@ -1038,112 +1149,247 @@ class Selection {
1038
1149
  container.cell.appendChild(divEle);
1039
1150
  }
1040
1151
  }
1152
+ /**
1153
+ * Selects or toggles checkboxes for the provided row indexes.
1154
+ *
1155
+ * @param {number[]} rowIndexes - Array of row indexes to toggle selection for.
1156
+ * @returns {void}
1157
+ */
1041
1158
  selectCheckboxes(rowIndexes) {
1042
- if (isNullOrUndefined(rowIndexes)) {
1043
- const error = 'The provided value for the rowIndexes is undefined. Please ensure the rowIndexes contains number.';
1044
- this.parent.trigger(actionFailure, { error: error });
1045
- }
1046
1159
  for (let i = 0; i < rowIndexes.length; i++) {
1047
- let record = this.parent.getCurrentViewRecords()[rowIndexes[parseInt(i.toString(), 10)]];
1048
- const flatRecord = getParentData(this.parent, record.uniqueID);
1049
- record = flatRecord;
1050
- const checkboxState = (record.checkboxState === 'uncheck') ? 'check' : 'uncheck';
1051
- record.checkboxState = checkboxState;
1052
- const keys = Object.keys(record);
1053
- for (let j = 0; j < keys.length; j++) {
1054
- if (Object.prototype.hasOwnProperty.call(flatRecord, keys[parseInt(j.toString(), 10)])) {
1055
- flatRecord[keys[parseInt(j.toString(), 10)]] = record[keys[parseInt(j.toString(), 10)]];
1056
- }
1057
- }
1058
- this.traverSelection(record, checkboxState, false);
1059
- if (this.parent.autoCheckHierarchy) {
1060
- this.headerSelection();
1061
- }
1160
+ const viewRec = this.parent.getCurrentViewRecords()[rowIndexes[parseInt(i.toString(), 10)]];
1161
+ const flatRec = getParentData(this.parent, viewRec.uniqueID);
1162
+ const nextState = (flatRec.checkboxState === 'check') ? 'uncheck' : 'check';
1163
+ flatRec.checkboxState = nextState;
1164
+ this.traverSelection(flatRec, nextState, false);
1062
1165
  }
1063
1166
  }
1064
- traverSelection(record, checkboxState, ischildItem) {
1065
- let length = 0;
1066
- this.updateSelectedItems(record, checkboxState);
1067
- if (!ischildItem && record.parentItem && this.parent.autoCheckHierarchy) {
1068
- this.updateParentSelection(record.parentItem);
1069
- }
1070
- if (record.childRecords && this.parent.autoCheckHierarchy) {
1071
- let childRecords = record.childRecords;
1072
- if (!isNullOrUndefined(this.parent.filterModule) &&
1073
- this.parent.filterModule.filteredResult.length > 0 && this.parent.autoCheckHierarchy) {
1074
- childRecords = this.getFilteredChildRecords(childRecords);
1075
- }
1076
- length = childRecords.length;
1077
- for (let count = 0; count < length; count++) {
1078
- if (!childRecords[parseInt(count.toString(), 10)].isSummaryRow) {
1079
- if (childRecords[parseInt(count.toString(), 10)].hasChildRecords) {
1080
- this.traverSelection(childRecords[parseInt(count.toString(), 10)], checkboxState, true);
1081
- }
1082
- else {
1083
- this.updateSelectedItems(childRecords[parseInt(count.toString(), 10)], checkboxState);
1084
- }
1167
+ /**
1168
+ * Traverses selection for a record and cascades selections to children/parents as necessary.
1169
+ *
1170
+ * @param {ITreeData} record - The record to process.
1171
+ * @param {string} checkboxState - The desired checkbox state ('check'|'uncheck'|'indeterminate').
1172
+ * @param {boolean} isChildItem - True if this invocation is for a child during recursion.
1173
+ * @returns {void}
1174
+ */
1175
+ traverSelection(record, checkboxState, isChildItem) {
1176
+ const previousState = record.checkboxState;
1177
+ if (!isChildItem) {
1178
+ this.buildVisibleUidMap();
1179
+ }
1180
+ let effectiveChildren = Array.isArray(record.childRecords) ? record.childRecords : [];
1181
+ if ((!effectiveChildren || effectiveChildren.length === 0) && this.parent.autoCheckHierarchy) {
1182
+ effectiveChildren = this.getChildrenFromFlat(record);
1183
+ }
1184
+ if (this.parent.filterModule && this.parent.filterModule.filteredResult.length > 0
1185
+ && effectiveChildren && effectiveChildren.length) {
1186
+ effectiveChildren = this.getFilteredChildRecords(effectiveChildren);
1187
+ }
1188
+ if (!this.parent.autoCheckHierarchy || !effectiveChildren || effectiveChildren.length === 0) {
1189
+ this.updateSelectedItems(record, checkboxState);
1190
+ if (!isChildItem) {
1191
+ if (record.parentItem && this.parent.autoCheckHierarchy) {
1192
+ this.updateParentSelection(record.parentItem);
1085
1193
  }
1194
+ this.updateSelectedCollectionsAfterBulk(this.resolveHeaderSelectionList(), '');
1195
+ this.refreshVisibleCheckboxes();
1196
+ if (this.parent.autoCheckHierarchy) {
1197
+ this.updateHeaderCheckboxState();
1198
+ }
1199
+ }
1200
+ return;
1201
+ }
1202
+ let childCount = 0;
1203
+ let checkedCount = 0;
1204
+ let indeterminateCount = 0;
1205
+ for (let i = 0; i < effectiveChildren.length; i++) {
1206
+ const child = effectiveChildren[parseInt(i.toString(), 10)];
1207
+ if (!child || child.isSummaryRow) {
1208
+ continue;
1209
+ }
1210
+ childCount++;
1211
+ this.updateSelectedItems(child, checkboxState, true);
1212
+ if (child.hasChildRecords) {
1213
+ this.traverSelection(child, checkboxState, true);
1086
1214
  }
1215
+ if (child.checkboxState === 'check') {
1216
+ checkedCount++;
1217
+ }
1218
+ else if (child.checkboxState === 'indeterminate') {
1219
+ indeterminateCount++;
1220
+ }
1221
+ }
1222
+ if (record.uniqueID) {
1223
+ this.parentSelectionCounters[record.uniqueID] = {
1224
+ total: childCount,
1225
+ checked: checkedCount,
1226
+ indeterminate: indeterminateCount
1227
+ };
1228
+ }
1229
+ const summary = this.parentSelectionCounters[record.uniqueID];
1230
+ let finalState = this.deriveParentState(record, summary);
1231
+ if (checkboxState === 'check' && summary.total > 0 && summary.checked === summary.total && summary.indeterminate === 0) {
1232
+ finalState = 'check';
1233
+ }
1234
+ this.updateSelectedItems(record, finalState);
1235
+ if (!isChildItem && record.parentItem && this.parent.autoCheckHierarchy) {
1236
+ this.updateParentSelection(record.parentItem, previousState, finalState);
1237
+ }
1238
+ if (!isChildItem) {
1239
+ const bulkList = this.resolveHeaderSelectionList();
1240
+ this.updateSelectedCollectionsAfterBulk(bulkList, ''); // This will rebuild selectedItems & selectedIndexes based on total state
1241
+ this.refreshVisibleCheckboxes();
1242
+ this.updateHeaderCheckboxState();
1087
1243
  }
1088
1244
  }
1245
+ /**
1246
+ * Filters provided child records against the current filter result.
1247
+ *
1248
+ * @param {ITreeData[]} childRecords - The array of child records to filter.
1249
+ * @returns {ITreeData[]} The filtered child records array.
1250
+ */
1089
1251
  getFilteredChildRecords(childRecords) {
1090
1252
  const filteredChildRecords = childRecords.filter((e) => {
1091
1253
  return this.parent.filterModule.filteredResult.indexOf(e) > -1;
1092
1254
  });
1093
1255
  return filteredChildRecords;
1094
1256
  }
1095
- updateParentSelection(parentRecord) {
1096
- let length = 0;
1097
- let childRecords = [];
1098
- const record = getParentData(this.parent, parentRecord.uniqueID);
1099
- if (record && record.childRecords) {
1100
- childRecords = record.childRecords;
1101
- }
1102
- if (!isNullOrUndefined(this.parent.filterModule) &&
1103
- this.parent.filterModule.filteredResult.length > 0 && this.parent.autoCheckHierarchy) {
1104
- childRecords = this.getFilteredChildRecords(childRecords);
1105
- }
1106
- length = childRecords && childRecords.length;
1107
- let indeter = 0;
1108
- let checkChildRecords = 0;
1109
- if (!isNullOrUndefined(record)) {
1110
- for (let i = 0; i < childRecords.length; i++) {
1111
- const currentRecord = getParentData(this.parent, childRecords[parseInt(i.toString(), 10)].uniqueID);
1112
- const checkBoxRecord = currentRecord;
1113
- if (!isNullOrUndefined(checkBoxRecord)) {
1114
- if (checkBoxRecord.checkboxState === 'indeterminate') {
1115
- indeter++;
1116
- }
1117
- else if (checkBoxRecord.checkboxState === 'check') {
1118
- checkChildRecords++;
1119
- }
1120
- }
1121
- }
1122
- if (indeter > 0 || (checkChildRecords > 0 && checkChildRecords !== length)) {
1123
- record.checkboxState = 'indeterminate';
1257
+ /**
1258
+ * Derives children for a record from flatData using the parentItem link.
1259
+ * Used when childRecords is missing or empty.
1260
+ *
1261
+ * @param {ITreeData} record - The record for which to find child elements.
1262
+ * @returns {ITreeData[]} An array of child records derived from flatData.
1263
+ */
1264
+ getChildrenFromFlat(record) {
1265
+ const all = (this.parent.flatData);
1266
+ if (!all || !record) {
1267
+ return [];
1268
+ }
1269
+ const pid = record.uniqueID;
1270
+ const out = [];
1271
+ for (let i = 0; i < all.length; i++) {
1272
+ const r = all[parseInt(i.toString(), 10)];
1273
+ if (!r || r.isSummaryRow) {
1274
+ continue;
1124
1275
  }
1125
- else if (checkChildRecords === 0 && (!record.hasFilteredChildRecords || isNullOrUndefined(record.hasFilteredChildRecords)) && !isNullOrUndefined(this.parent['dataResults']['actionArgs']) &&
1126
- (this.parent['dataResults']['actionArgs'].requestType === 'searching' || this.parent['dataResults']['actionArgs'].requestType === 'filtering') && record.checkboxState === 'check') {
1127
- record.checkboxState = 'check';
1276
+ const p = r.parentItem;
1277
+ if (p && p.uniqueID === pid) {
1278
+ out.push(r);
1128
1279
  }
1129
- else if ((checkChildRecords === 0 && indeter === 0) || (checkChildRecords === 0 && record.hasFilteredChildRecords && !isNullOrUndefined(this.parent['dataResults']['actionArgs']) &&
1130
- (this.parent['dataResults']['actionArgs'].requestType === 'searching' || this.parent['dataResults']['actionArgs'].requestType === 'filtering') && record.checkboxState === 'check')) {
1131
- record.checkboxState = 'uncheck';
1280
+ }
1281
+ return out;
1282
+ }
1283
+ /**
1284
+ * Updates parent selection by rebuilding summary and applying deltas, then bubbling up if required.
1285
+ *
1286
+ * @param {ITreeData} parentRecord - The parent record reference.
1287
+ * @param {string} [previousChildState] - Previous state of the child that changed.
1288
+ * @param {string} [nextChildState] - Next state of the child that changed.
1289
+ * @returns {void}
1290
+ */
1291
+ updateParentSelection(parentRecord, previousChildState, nextChildState) {
1292
+ const parent = getParentData(this.parent, parentRecord.uniqueID);
1293
+ if (!parent) {
1294
+ return;
1295
+ }
1296
+ const summary = this.buildSelectionSummary(parent);
1297
+ if (previousChildState) {
1298
+ this.applySummaryDelta(summary, previousChildState, -1);
1299
+ }
1300
+ if (nextChildState) {
1301
+ this.applySummaryDelta(summary, nextChildState, 1);
1302
+ }
1303
+ if (parent.uniqueID) {
1304
+ this.parentSelectionCounters[parent.uniqueID] = summary;
1305
+ }
1306
+ const desiredState = this.deriveParentState(parent, summary);
1307
+ if (parent.checkboxState === desiredState) {
1308
+ return;
1309
+ }
1310
+ const parentPrev = parent.checkboxState;
1311
+ parent.checkboxState = desiredState;
1312
+ this.updateSelectedItems(parent, desiredState);
1313
+ if (parent.parentItem) {
1314
+ this.updateParentSelection(parent.parentItem, parentPrev, desiredState);
1315
+ }
1316
+ }
1317
+ /**
1318
+ * Builds a selection summary for a record's children.
1319
+ *
1320
+ * @param {Object} record - The record whose children should be summarized.
1321
+ * @param {boolean} [ignoreFilter] - If true, ignore current filter when computing summary.
1322
+ * @returns {{ total: number, checked: number, indeterminate: number }} The computed summary.
1323
+ */
1324
+ buildSelectionSummary(record, ignoreFilter) {
1325
+ const summary = { total: 0, checked: 0, indeterminate: 0 };
1326
+ let children = [];
1327
+ if (record && Array.isArray(record.childRecords) && record.childRecords.length) {
1328
+ children = record.childRecords;
1329
+ }
1330
+ else {
1331
+ children = this.getChildrenFromFlat(record);
1332
+ }
1333
+ if (!ignoreFilter && this.parent.filterModule && this.parent.filterModule.filteredResult.length > 0) {
1334
+ children = this.getFilteredChildRecords(children);
1335
+ }
1336
+ for (let i = 0; i < children.length; i++) {
1337
+ const child = children[parseInt(i.toString(), 10)];
1338
+ if (!child || child.isSummaryRow) {
1339
+ continue;
1132
1340
  }
1133
- else {
1134
- record.checkboxState = 'check';
1341
+ summary.total++;
1342
+ if (child.checkboxState === 'check') {
1343
+ summary.checked++;
1135
1344
  }
1136
- this.updateSelectedItems(record, record.checkboxState);
1137
- if (record.parentItem) {
1138
- this.updateParentSelection(record.parentItem);
1345
+ else if (child.checkboxState === 'indeterminate') {
1346
+ summary.indeterminate++;
1139
1347
  }
1140
1348
  }
1349
+ return summary;
1350
+ }
1351
+ /**
1352
+ * Applies a delta to a selection summary based on a state change.
1353
+ *
1354
+ * @param {Object} summary - The summary to modify. Object with numeric properties: total, checked, indeterminate.
1355
+ * @param {string} state - The state that changed ('check' | 'indeterminate').
1356
+ * @param {number} delta - The delta to apply (e.g. +1 or -1).
1357
+ * @returns {void}
1358
+ */
1359
+ applySummaryDelta(summary, state, delta) {
1360
+ if (state === 'check') {
1361
+ summary.checked = Math.max(0, summary.checked + delta);
1362
+ }
1363
+ else if (state === 'indeterminate') {
1364
+ summary.indeterminate = Math.max(0, summary.indeterminate + delta);
1365
+ }
1141
1366
  }
1367
+ /**
1368
+ * Derives the parent's checkbox state based on children summary counts.
1369
+ *
1370
+ * @param {ITreeData} record The parent record.
1371
+ * @param {{ total: number, checked: number, indeterminate: number }} summary The children summary.
1372
+ * @returns {'check'|'indeterminate'|'uncheck'} The derived checkbox state.
1373
+ */
1374
+ deriveParentState(record, summary) {
1375
+ const total = summary.total;
1376
+ const checked = summary.checked;
1377
+ const indeterminate = summary.indeterminate;
1378
+ if (indeterminate > 0 || (checked > 0 && checked !== total)) {
1379
+ return 'indeterminate';
1380
+ }
1381
+ if (checked === total && total > 0) {
1382
+ return 'check';
1383
+ }
1384
+ return 'uncheck';
1385
+ }
1386
+ /**
1387
+ * Handles header checkbox (select all / clear all) behavior.
1388
+ *
1389
+ * @param {boolean} [checkAll] - Optional explicit flag to check or uncheck all.
1390
+ * @returns {void}
1391
+ */
1142
1392
  headerSelection(checkAll) {
1143
- let index = -1;
1144
- let length = 0;
1145
- //This property used to maintain the check state of the currentview data after clear filtering
1146
- let multiFilterCheckState = false;
1147
1393
  if (!isNullOrUndefined(this.parent.filterModule) && this.parent.filterModule.filteredResult.length > 0) {
1148
1394
  const filterResult = this.parent.filterModule.filteredResult;
1149
1395
  if (this.filteredList.length === 0) {
@@ -1153,160 +1399,397 @@ class Selection {
1153
1399
  this.searchingRecords = filterResult;
1154
1400
  }
1155
1401
  else {
1156
- if (this.filteredList !== filterResult) {
1402
+ if (this.filteredList !== filterResult && !this.parent.grid.searchSettings.key.length) {
1157
1403
  this.filteredList = filterResult;
1158
- multiFilterCheckState = true;
1159
- }
1160
- else {
1161
- multiFilterCheckState = false;
1404
+ this.searchingRecords = [];
1162
1405
  }
1163
1406
  }
1164
1407
  }
1165
- if (this.filteredList.length > 0) {
1166
- if (!this.parent.filterSettings.columns.length && this.filteredList.length && !this.parent.grid.searchSettings.key.length) {
1167
- this.filteredList = [];
1408
+ if (this.searchingRecords.length > 0 && !isNullOrUndefined(checkAll)) {
1409
+ this.filteredList = this.searchingRecords;
1410
+ }
1411
+ else if (this.filteredList.length > 0 && !this.parent.filterSettings.columns.length
1412
+ && !this.parent.grid.searchSettings.key.length) {
1413
+ this.filteredList = [];
1414
+ }
1415
+ const records = this.resolveHeaderSelectionList(true);
1416
+ if (!isNullOrUndefined(checkAll)) {
1417
+ this.resetSelectionCaches();
1418
+ const targetState = checkAll ? 'check' : 'uncheck';
1419
+ this.headerSelectionState = targetState;
1420
+ this.processHeaderSelection(records, targetState);
1421
+ this.finalizeParentsAfterBulk(records);
1422
+ this.updateSelectedCollectionsAfterBulk(records, '');
1423
+ this.refreshVisibleCheckboxes();
1424
+ this.updateHeaderCheckboxState();
1425
+ return;
1426
+ }
1427
+ this.totalSelectableCount = this.countSelectableRecords(records);
1428
+ this.updateHeaderCheckboxState();
1429
+ }
1430
+ /**
1431
+ * Finalizes parent states after a bulk header operation (e.g., Select All).
1432
+ * This ensures parent states (checked/indeterminate) are correct after cascades.
1433
+ *
1434
+ * @param {ITreeData[]} records - The records that were processed in the bulk operation.
1435
+ * @returns {void}
1436
+ */
1437
+ finalizeParentsAfterBulk(records) {
1438
+ const all = records;
1439
+ for (let i = 0; i < all.length; i++) {
1440
+ const rec = all[parseInt(i.toString(), 10)];
1441
+ if (!rec || !rec.hasChildRecords) {
1442
+ continue;
1168
1443
  }
1169
- if (this.searchingRecords.length && !isNullOrUndefined(checkAll)) {
1170
- this.filteredList = this.searchingRecords;
1444
+ const summary = this.buildSelectionSummary(rec, true);
1445
+ this.parentSelectionCounters[rec.uniqueID] = summary;
1446
+ let finalState = this.deriveParentState(rec, summary);
1447
+ if (this.headerSelectionState === 'check' &&
1448
+ summary.total > 0 && summary.checked === summary.total && summary.indeterminate === 0) {
1449
+ finalState = 'check';
1450
+ }
1451
+ else if (this.headerSelectionState === 'uncheck') {
1452
+ finalState = 'uncheck';
1453
+ }
1454
+ if (rec.checkboxState !== finalState) {
1455
+ this.updateSelectedItems(rec, finalState);
1171
1456
  }
1172
1457
  }
1173
- let data;
1174
- if (!(isNullOrUndefined(this.parent.filterModule)) &&
1175
- this.parent.filterModule.filteredResult.length === 0 && this.parent.getCurrentViewRecords().length === 0 &&
1176
- this.parent.filterSettings.columns.length > 0) {
1177
- data = this.filteredList;
1178
- }
1179
- else {
1180
- data = (!isNullOrUndefined(this.parent.filterModule) &&
1181
- (this.filteredList.length > 0)) ? this.filteredList : this.parent.flatData;
1458
+ }
1459
+ /**
1460
+ * Processes header selection for each record, setting their state silently in the data model.
1461
+ * Called during bulk operations like "select all".
1462
+ *
1463
+ * @param {ITreeData[]} records - The records to process.
1464
+ * @param {string} targetState - The target state to set on each record.
1465
+ * @returns {void}
1466
+ */
1467
+ processHeaderSelection(records, targetState) {
1468
+ for (let i = 0; i < records.length; i++) {
1469
+ const record = records[parseInt(i.toString(), 10)];
1470
+ if (!record) {
1471
+ continue;
1472
+ }
1473
+ const previousState = record.checkboxState;
1474
+ if (previousState === targetState) {
1475
+ continue;
1476
+ }
1477
+ record.checkboxState = targetState;
1478
+ this.updateSelectedItems(record, targetState, true);
1182
1479
  }
1183
- data = isRemoteData(this.parent) ? this.parent.getCurrentViewRecords() : data;
1184
- if (!isNullOrUndefined(checkAll)) {
1185
- for (let i = 0; i < data.length; i++) {
1186
- if (checkAll) {
1187
- if (data[parseInt(i.toString(), 10)].checkboxState === 'check') {
1480
+ }
1481
+ /**
1482
+ * Rebuilds `selectedItems`, `selectedUidMap`, and `selectedIndexes` based on the current data states in the model.
1483
+ * This method is called after bulk operations (like headerSelection, grid actions, etc.) to synchronize internal collections.
1484
+ * It ensures `selectedItems` retains original selection order *as much as possible* for currently checked items
1485
+ * and `selectedIndexes` reflects their *current visible order*.
1486
+ *
1487
+ * @param {ITreeData[]} records - The records that were processed (or the full data set if re-evaluating everything).
1488
+ * @param {string} requestType - The data action type such as filtering, searching, refresh,etc.
1489
+ * @returns {void}
1490
+ */
1491
+ updateSelectedCollectionsAfterBulk(records, requestType) {
1492
+ const hasFilter = !!(this.parent.filterModule && this.parent.filterModule.filteredResult &&
1493
+ this.parent.filterModule.filteredResult.length);
1494
+ const hasSearch = !!(this.parent.grid && this.parent.grid.searchSettings &&
1495
+ this.parent.grid.searchSettings.key && this.parent.grid.searchSettings.key.length);
1496
+ const isFilterOrSearch = hasFilter || hasSearch || requestType === 'refresh' || requestType === 'searching';
1497
+ const currentlySelectedItemsInOrder = isFilterOrSearch ? records : this.selectedItems.slice();
1498
+ const newSelectedItems = [];
1499
+ const newSelectedUidMap = new Map();
1500
+ const newSelectedIndexes = [];
1501
+ for (const item of currentlySelectedItemsInOrder) {
1502
+ if (item.hasChildRecords && isFilterOrSearch && item.level === 0) {
1503
+ this.updateParentSelection(item);
1504
+ }
1505
+ if (item.uniqueID && item.checkboxState === 'check') {
1506
+ newSelectedItems.push(item);
1507
+ newSelectedUidMap.set(item.uniqueID, true);
1508
+ }
1509
+ }
1510
+ if (!isFilterOrSearch) {
1511
+ const allFlatData = this.parent.flatData;
1512
+ if (allFlatData) {
1513
+ for (const record of allFlatData) {
1514
+ if (!record || record.isSummaryRow) {
1188
1515
  continue;
1189
1516
  }
1190
- if (multiFilterCheckState) {
1191
- continue;
1517
+ if (record.uniqueID && record.checkboxState === 'check' && !newSelectedUidMap.has(record.uniqueID)) {
1518
+ newSelectedItems.push(record);
1519
+ newSelectedUidMap.set(record.uniqueID, true);
1192
1520
  }
1193
- data[parseInt(i.toString(), 10)].checkboxState = 'check';
1194
- this.updateSelectedItems(data[parseInt(i.toString(), 10)], data[parseInt(i.toString(), 10)].checkboxState);
1195
1521
  }
1196
- else {
1197
- index = this.selectedItems.indexOf(data[parseInt(i.toString(), 10)]);
1198
- if (index > -1) {
1199
- data[parseInt(i.toString(), 10)].checkboxState = 'uncheck';
1200
- this.updateSelectedItems(data[parseInt(i.toString(), 10)], data[parseInt(i.toString(), 10)].checkboxState);
1201
- if (this.parent.autoCheckHierarchy) {
1202
- this.updateParentSelection(data[parseInt(i.toString(), 10)]);
1203
- }
1522
+ }
1523
+ }
1524
+ this.selectedItems = newSelectedItems;
1525
+ this.selectedUidMap = newSelectedUidMap;
1526
+ this.buildVisibleUidMap();
1527
+ for (const item of this.selectedItems) {
1528
+ const visibleIdx = this.visibleUidIndex[item.uniqueID];
1529
+ if (visibleIdx !== undefined) {
1530
+ newSelectedIndexes.push(visibleIdx);
1531
+ }
1532
+ }
1533
+ this.selectedIndexes = newSelectedIndexes;
1534
+ this.checkedItemCount = this.selectedItems.length;
1535
+ this.totalSelectableCount =
1536
+ this.countSelectableRecords(records);
1537
+ }
1538
+ /**
1539
+ * Refreshes visible checkbox DOM elements to reflect the current data state.
1540
+ * This method exclusively updates the UI representation of checkboxes.
1541
+ *
1542
+ * @returns {void}
1543
+ */
1544
+ refreshVisibleCheckboxes() {
1545
+ this.buildVisibleUidMap();
1546
+ const data = this.parent.getCurrentViewRecords();
1547
+ const uidMap = this.parent.uniqueIDCollection;
1548
+ for (let i = 0; data && i < data.length; i++) {
1549
+ const viewRec = data[parseInt(i.toString(), 10)];
1550
+ if (!viewRec) {
1551
+ continue;
1552
+ }
1553
+ const uid = viewRec.uniqueID;
1554
+ const srcRec = (uidMap && uid != null) ? uidMap[String(uid)] : viewRec;
1555
+ const state = (srcRec && srcRec.checkboxState) ? srcRec.checkboxState : 'uncheck';
1556
+ let rowEl = null;
1557
+ const rowUid = viewRec.uid;
1558
+ if (rowUid) {
1559
+ rowEl = this.parent.grid.getRowElementByUID(rowUid);
1560
+ }
1561
+ if (!rowEl) {
1562
+ const rows = this.parent.getRows();
1563
+ rowEl = rows && rows[parseInt(i.toString(), 10)];
1564
+ if ((this.parent.frozenRows || this.parent.getFrozenColumns()) && !rowEl) {
1565
+ const movableRows = this.parent.getDataRows();
1566
+ rowEl = movableRows && movableRows[parseInt(i.toString(), 10)];
1567
+ }
1568
+ }
1569
+ if (rowEl) {
1570
+ const frame = rowEl.querySelector('.e-hierarchycheckbox .e-frame');
1571
+ if (frame) {
1572
+ removeClass([frame], ['e-check', 'e-stop', 'e-uncheck']);
1573
+ frame.classList.add(state === 'indeterminate' ? 'e-stop' : ('e-' + state));
1574
+ const input = rowEl.querySelector('.e-treecheckselect');
1575
+ if (input) {
1576
+ input.setAttribute('aria-checked', state === 'check' ? 'true' :
1577
+ (state === 'uncheck' ? 'false' : 'mixed'));
1204
1578
  }
1205
1579
  }
1206
1580
  }
1207
1581
  }
1208
- if (checkAll === false && this.parent.enableVirtualization) {
1209
- this.selectedItems = [];
1210
- this.selectedIndexes = [];
1211
- data.filter((rec) => {
1212
- rec.checkboxState = 'uncheck';
1213
- this.updateSelectedItems(rec, rec.checkboxState);
1214
- });
1582
+ }
1583
+ /**
1584
+ * Resets internal selection caches to their initial state.
1585
+ * This is usually called before a bulk selection operation (like "select all").
1586
+ *
1587
+ * @returns {void}
1588
+ */
1589
+ resetSelectionCaches() {
1590
+ this.parentSelectionCounters = {};
1591
+ this.selectedUidMap = new Map();
1592
+ this.selectedItems = [];
1593
+ this.selectedIndexes = [];
1594
+ this.totalSelectableCount = 0;
1595
+ this.headerSelectionState = 'uncheck';
1596
+ this.checkedItemCount = 0;
1597
+ }
1598
+ /**
1599
+ * Counts selectable (non-summary) records in the provided array.
1600
+ *
1601
+ * @param {ITreeData[]} records - The records to count.
1602
+ * @returns {number} The number of selectable records.
1603
+ */
1604
+ countSelectableRecords(records) {
1605
+ let count = 0;
1606
+ if (!records) {
1607
+ return count;
1215
1608
  }
1216
- length = this.selectedItems.length;
1217
- const checkbox = this.parent.getHeaderContent().querySelectorAll('.e-frame')[0];
1218
- if (length > 0 && data.length > 0) {
1219
- if (length !== data.length && !checkAll) {
1220
- removeClass([checkbox], ['e-check']);
1221
- checkbox.classList.add('e-stop');
1609
+ for (let i = 0; i < records.length; i++) {
1610
+ const rec = records[parseInt(i.toString(), 10)];
1611
+ if (rec && !rec.isSummaryRow) {
1612
+ count++;
1613
+ }
1614
+ }
1615
+ return count;
1616
+ }
1617
+ /**
1618
+ * Resolves the list of records used for header selection operations (e.g., for `select all`).
1619
+ *
1620
+ * @param {boolean} [includeAll] - If true and data is local, returns flatData (all records for full dataset actions).
1621
+ * @returns {ITreeData[]} The array of records to consider for header operations.
1622
+ */
1623
+ resolveHeaderSelectionList(includeAll) {
1624
+ let dataToProcess = [];
1625
+ if (!isRemoteData(this.parent)) {
1626
+ const hasFilter = !!(this.parent.filterModule &&
1627
+ this.parent.filterModule.filteredResult &&
1628
+ this.parent.filterModule.filteredResult.length);
1629
+ const hasSearch = !!(this.parent.grid &&
1630
+ this.parent.grid.searchSettings &&
1631
+ this.parent.grid.searchSettings.key &&
1632
+ this.parent.grid.searchSettings.key.length);
1633
+ if (includeAll) {
1634
+ if (hasFilter) {
1635
+ dataToProcess = this.filteredList && this.filteredList.length
1636
+ ? this.filteredList
1637
+ : this.parent.filterModule.filteredResult;
1638
+ }
1639
+ else if (hasSearch && this.searchingRecords && this.searchingRecords.length) {
1640
+ dataToProcess = this.searchingRecords;
1641
+ }
1642
+ else {
1643
+ dataToProcess = this.parent.flatData;
1644
+ }
1222
1645
  }
1223
1646
  else {
1224
- removeClass([checkbox], ['e-stop']);
1225
- checkbox.classList.add('e-check');
1647
+ if (hasFilter) {
1648
+ dataToProcess = this.filteredList && this.filteredList.length
1649
+ ? this.filteredList
1650
+ : this.parent.filterModule.filteredResult;
1651
+ }
1652
+ else if (hasSearch && this.searchingRecords && this.searchingRecords.length) {
1653
+ dataToProcess = this.searchingRecords;
1654
+ }
1655
+ else {
1656
+ dataToProcess = this.parent.flatData;
1657
+ }
1226
1658
  }
1227
1659
  }
1228
1660
  else {
1229
- removeClass([checkbox], ['e-check', 'e-stop']);
1661
+ dataToProcess = this.parent.getCurrentViewRecords();
1230
1662
  }
1663
+ return dataToProcess;
1231
1664
  }
1232
- updateSelectedItems(currentRecord, checkState) {
1233
- const record = this.parent.grid.currentViewData.filter((e) => {
1234
- return e.uniqueID === currentRecord.uniqueID;
1235
- });
1236
- let checkedRecord;
1237
- const recordIndex = this.parent.grid.currentViewData.indexOf(record[0]);
1238
- const checkboxRecord = getParentData(this.parent, currentRecord.uniqueID);
1239
- const tr = this.parent.getRows()[parseInt(recordIndex.toString(), 10)];
1240
- let checkbox;
1241
- if (recordIndex > -1) {
1242
- let movableTr;
1243
- if (this.parent.frozenRows || this.parent.getFrozenColumns()) {
1244
- movableTr = this.parent.getDataRows()[parseInt(recordIndex.toString(), 10)];
1245
- }
1246
- checkbox = tr.querySelectorAll('.e-hierarchycheckbox .e-frame')[0] ? tr.querySelectorAll('.e-hierarchycheckbox .e-frame')[0]
1247
- : movableTr.querySelectorAll('.e-hierarchycheckbox .e-frame')[0];
1248
- if (!isNullOrUndefined(checkbox)) {
1249
- removeClass([checkbox], ['e-check', 'e-stop', 'e-uncheck']);
1665
+ /**
1666
+ * Updates the header checkbox state (checked/indeterminate/unchecked) based on current selections.
1667
+ *
1668
+ * @returns {void}
1669
+ */
1670
+ updateHeaderCheckboxState() {
1671
+ const frame = this.headerCheckboxFrameEl;
1672
+ if (!frame) {
1673
+ return;
1674
+ }
1675
+ const recordsForHeaderLogic = this.resolveHeaderSelectionList(true);
1676
+ this.totalSelectableCount = this.countSelectableRecords(recordsForHeaderLogic);
1677
+ let checkedCountForHeaderLogic = 0;
1678
+ for (const record of recordsForHeaderLogic) {
1679
+ if (record && !record.isSummaryRow && record.checkboxState === 'check') {
1680
+ checkedCountForHeaderLogic++;
1250
1681
  }
1251
1682
  }
1252
- checkedRecord = checkboxRecord;
1253
- if (isNullOrUndefined(checkedRecord)) {
1254
- checkedRecord = currentRecord;
1683
+ removeClass([frame], ['e-check', 'e-stop', 'e-uncheck']);
1684
+ if (this.totalSelectableCount === 0) {
1685
+ frame.classList.add('e-uncheck');
1255
1686
  }
1256
- checkedRecord.checkboxState = checkState;
1257
- if (checkState === 'check' && isNullOrUndefined(currentRecord.isSummaryRow)) {
1258
- if (recordIndex !== -1 && this.selectedIndexes.indexOf(recordIndex) === -1) {
1259
- this.selectedIndexes.push(recordIndex);
1260
- }
1261
- if (this.selectedItems.indexOf(checkedRecord) === -1 && (recordIndex !== -1 &&
1262
- (!isNullOrUndefined(this.parent.filterModule) && this.parent.filterModule.filteredResult.length > 0))) {
1263
- this.selectedItems.push(checkedRecord);
1264
- }
1265
- if (this.selectedItems.indexOf(checkedRecord) === -1 && (this.parent.enableVirtualization || this.parent.allowPaging) && ((!isNullOrUndefined(this.parent.filterModule) && this.parent.filterModule.filteredResult.length > 0))) {
1266
- this.selectedItems.push(checkedRecord);
1267
- }
1268
- if (this.selectedItems.indexOf(checkedRecord) === -1 && (!isNullOrUndefined(this.parent.filterModule) &&
1269
- this.parent.filterModule.filteredResult.length === 0)) {
1270
- this.selectedItems.push(checkedRecord);
1687
+ else if (checkedCountForHeaderLogic === 0) {
1688
+ frame.classList.add('e-uncheck');
1689
+ }
1690
+ else if (checkedCountForHeaderLogic === this.totalSelectableCount) {
1691
+ frame.classList.add('e-check');
1692
+ }
1693
+ else {
1694
+ frame.classList.add('e-stop');
1695
+ }
1696
+ }
1697
+ /**
1698
+ * Updates selection arrays (selectedItems, selectedUidMap, selectedIndexes) and visible DOM for a single record.
1699
+ * This is the core method for managing the state of a single checkbox.
1700
+ *
1701
+ * @param {ITreeData} currentRecord - The record to update.
1702
+ * @param {string} checkState - The new checkbox state ('check' | 'uncheck' | 'indeterminate').
1703
+ * @param {boolean} [silent] - If true, update is silent (only updates data model, no collection management or DOM update).
1704
+ * @returns {void}
1705
+ */
1706
+ updateSelectedItems(currentRecord, checkState, silent) {
1707
+ this.buildVisibleUidMap();
1708
+ const uid = currentRecord.uniqueID;
1709
+ const uidMap = this.parent.uniqueIDCollection;
1710
+ const checkboxRecord = (uidMap && uid != null) ? (uidMap[String(uid)] ?
1711
+ uidMap[String(uid)] : currentRecord) : currentRecord;
1712
+ const isSummary = currentRecord.isSummaryRow === true;
1713
+ const previousState = checkboxRecord.checkboxState;
1714
+ const currentVisibleIndex = this.visibleUidIndex[String(uid)];
1715
+ checkboxRecord.checkboxState = checkState;
1716
+ if (silent) {
1717
+ return;
1718
+ }
1719
+ if (!isSummary && previousState !== checkState) {
1720
+ if (checkState === 'check') {
1721
+ this.checkedItemCount++;
1722
+ if (!this.selectedUidMap.has(String(uid))) {
1723
+ if (checkboxRecord.uniqueID) {
1724
+ this.selectedUidMap.set(String(checkboxRecord.uniqueID), true);
1725
+ }
1726
+ this.selectedItems.push(checkboxRecord);
1727
+ if (currentVisibleIndex !== undefined && this.selectedIndexes.indexOf(currentVisibleIndex) === -1) {
1728
+ this.selectedIndexes.push(currentVisibleIndex);
1729
+ }
1730
+ }
1271
1731
  }
1272
- if (this.selectedItems.indexOf(checkedRecord) === -1 && isNullOrUndefined(this.parent.filterModule)) {
1273
- this.selectedItems.push(checkedRecord);
1732
+ else if (previousState === 'check' || previousState === 'indeterminate') {
1733
+ if (this.checkedItemCount > 0) {
1734
+ this.checkedItemCount--;
1735
+ }
1736
+ if (checkboxRecord && checkboxRecord.uniqueID && this.selectedUidMap.has(String(checkboxRecord.uniqueID))) {
1737
+ this.selectedUidMap.delete(String(checkboxRecord.uniqueID));
1738
+ const itemIdx = this.selectedItems.indexOf(checkboxRecord);
1739
+ if (itemIdx !== -1) {
1740
+ this.selectedItems.splice(itemIdx, 1);
1741
+ }
1742
+ if (currentVisibleIndex !== undefined) {
1743
+ const indexInSelectedIndexes = this.selectedIndexes.indexOf(currentVisibleIndex);
1744
+ if (indexInSelectedIndexes > -1) {
1745
+ this.selectedIndexes.splice(indexInSelectedIndexes, 1);
1746
+ }
1747
+ }
1748
+ }
1274
1749
  }
1275
1750
  }
1276
- else if ((checkState === 'uncheck' || checkState === 'indeterminate') && isNullOrUndefined(currentRecord.isSummaryRow)) {
1277
- const index = this.selectedItems.indexOf(checkedRecord);
1278
- if (index !== -1) {
1279
- this.selectedItems.splice(index, 1);
1280
- }
1281
- if (this.selectedIndexes.indexOf(recordIndex) !== -1) {
1282
- const checkedIndex = this.selectedIndexes.indexOf(recordIndex);
1283
- this.selectedIndexes.splice(checkedIndex, 1);
1751
+ let rowEl = null;
1752
+ const rowUid = currentRecord.uid;
1753
+ if (rowUid) {
1754
+ rowEl = this.parent.grid.getRowElementByUID(rowUid);
1755
+ }
1756
+ if (!rowEl) {
1757
+ const recordVisibleIndex = currentVisibleIndex !== undefined ? currentVisibleIndex : (typeof this.visibleUidIndex[String(uid)] === 'number' ? this.visibleUidIndex[String(uid)] : -1);
1758
+ if (recordVisibleIndex > -1) {
1759
+ rowEl = this.parent.getRows()[parseInt(recordVisibleIndex.toString(), 10)];
1760
+ if (!rowEl && (this.parent.frozenRows || this.parent.getFrozenColumns())) {
1761
+ rowEl = this.parent.getDataRows()[parseInt(recordVisibleIndex.toString(), 10)];
1762
+ }
1284
1763
  }
1285
1764
  }
1286
- const checkBoxclass = checkState === 'indeterminate' ? 'e-stop' : 'e-' + checkState;
1287
- if (recordIndex > -1) {
1288
- if (!isNullOrUndefined(checkbox)) {
1289
- checkbox.classList.add(checkBoxclass);
1290
- tr.querySelector('.e-treecheckselect').setAttribute('aria-checked', checkState === 'check' ? 'true' : checkState === 'uncheck' ? 'false' : 'mixed');
1765
+ if (rowEl) {
1766
+ const frame = rowEl.querySelector('.e-hierarchycheckbox .e-frame');
1767
+ if (frame) {
1768
+ removeClass([frame], ['e-check', 'e-stop', 'e-uncheck']);
1769
+ frame.classList.add(checkState === 'indeterminate' ? 'e-stop' : ('e-' + checkState));
1770
+ }
1771
+ const input = rowEl.querySelector('.e-treecheckselect');
1772
+ if (input) {
1773
+ input.setAttribute('aria-checked', checkState === 'check' ? 'true' :
1774
+ (checkState === 'uncheck' ? 'false' : 'mixed'));
1291
1775
  }
1292
1776
  }
1293
1777
  }
1778
+ /**
1779
+ * Handles various grid actions and updates selection state accordingly.
1780
+ * This method ensures that selection state is maintained and UI is refreshed after grid operations.
1781
+ *
1782
+ * @param {CellSaveEventArgs} args - Action arguments containing requestType and data.
1783
+ * @returns {void}
1784
+ */
1294
1785
  updateGridActions(args) {
1295
1786
  const requestType = args.requestType;
1296
- let childData;
1297
- let childLength;
1298
1787
  if (isCheckboxcolumn(this.parent)) {
1299
1788
  if (this.parent.autoCheckHierarchy) {
1300
1789
  if ((requestType === 'sorting' || requestType === 'paging')) {
1301
- const rows = this.parent.grid.getRows();
1302
- childData = this.parent.getCurrentViewRecords();
1303
- childLength = childData.length;
1304
- this.selectedIndexes = [];
1305
- for (let i = 0; i < childLength; i++) {
1306
- if (!rows[parseInt(i.toString(), 10)].classList.contains('e-summaryrow')) {
1307
- this.updateSelectedItems(childData[parseInt(i.toString(), 10)], childData[parseInt(i.toString(), 10)].checkboxState);
1308
- }
1309
- }
1790
+ this.updateSelectedCollectionsAfterBulk(this.resolveHeaderSelectionList(), '');
1791
+ this.refreshVisibleCheckboxes();
1792
+ this.updateHeaderCheckboxState();
1310
1793
  }
1311
1794
  else if (requestType === 'delete' || args.action === 'add') {
1312
1795
  let updatedData = [];
@@ -1318,71 +1801,68 @@ class Selection {
1318
1801
  }
1319
1802
  for (let i = 0; i < updatedData.length; i++) {
1320
1803
  if (requestType === 'delete') {
1321
- const index = this.parent.flatData.indexOf(updatedData[parseInt(i.toString(), 10)]);
1322
- const checkedIndex = this.selectedIndexes.indexOf(index);
1323
- this.selectedIndexes.splice(checkedIndex, 1);
1324
- this.updateSelectedItems(updatedData[parseInt(i.toString(), 10)], 'uncheck');
1804
+ this.updateSelectedItems(updatedData[parseInt(i.toString(), 10)], 'uncheck', false);
1325
1805
  }
1326
1806
  if (!isNullOrUndefined(updatedData[parseInt(i.toString(), 10)].parentItem)) {
1327
1807
  this.updateParentSelection(updatedData[parseInt(i.toString(), 10)].parentItem);
1328
1808
  }
1329
1809
  }
1810
+ this.updateSelectedCollectionsAfterBulk(this.resolveHeaderSelectionList(true), '');
1811
+ this.refreshVisibleCheckboxes();
1812
+ if (this.parent.autoCheckHierarchy) {
1813
+ this.updateHeaderCheckboxState();
1814
+ }
1330
1815
  }
1331
1816
  else if (args.requestType === 'add' && this.parent.autoCheckHierarchy) {
1332
1817
  args.data.checkboxState = 'uncheck';
1333
1818
  }
1334
- else if (requestType === 'filtering' || requestType === 'searching' || requestType === 'refresh'
1335
- && !isRemoteData(this.parent)) {
1336
- this.selectedItems = [];
1337
- this.selectedIndexes = [];
1338
- childData = (!isNullOrUndefined(this.parent.filterModule) && this.parent.filterModule.filteredResult.length > 0) ?
1339
- this.parent.filterModule.filteredResult : this.parent.flatData;
1340
- childData.forEach((record) => {
1341
- if (this.parent.enableVirtualization) {
1342
- if (record.hasChildRecords && record.childRecords.length > 0) {
1343
- this.updateParentSelection(record);
1344
- }
1345
- else {
1346
- this.updateSelectedItems(record, record.checkboxState);
1347
- }
1348
- let child = findChildrenRecords(record);
1349
- child = this.getFilteredChildRecords(child);
1350
- for (let i = 0; i < child.length; i++) {
1351
- if (child[parseInt(i.toString(), 10)].hasChildRecords) {
1352
- this.updateParentSelection(child[parseInt(i.toString(), 10)]);
1353
- }
1354
- else if (!(child[parseInt(i.toString(), 10)].hasChildRecords) &&
1355
- !isNullOrUndefined(child[parseInt(i.toString(), 10)])) {
1356
- this.updateSelectedItems(child[parseInt(i.toString(), 10)], child[parseInt(i.toString(), 10)].checkboxState);
1357
- }
1358
- }
1359
- }
1360
- else {
1361
- if (record.hasChildRecords) {
1362
- this.updateParentSelection(record);
1363
- }
1364
- else {
1365
- this.updateSelectedItems(record, record.checkboxState);
1366
- }
1367
- }
1368
- });
1369
- this.headerSelection();
1819
+ else if (requestType === 'filtering' || requestType === 'searching' || requestType === 'refresh') {
1820
+ this.updateSelectedCollectionsAfterBulk(this.resolveHeaderSelectionList(), requestType);
1821
+ this.refreshVisibleCheckboxes();
1822
+ if (this.parent.autoCheckHierarchy) {
1823
+ this.updateHeaderCheckboxState();
1824
+ }
1370
1825
  }
1371
1826
  }
1372
1827
  else {
1373
- if ((requestType === 'filtering' || requestType === 'searching' || requestType === 'refresh')
1374
- && !isRemoteData(this.parent)) {
1828
+ if ((requestType === 'filtering' || requestType === 'searching' || requestType === 'refresh' ||
1829
+ requestType === 'sorting' || requestType === 'paging' || requestType === 'expanding' ||
1830
+ requestType === 'expand' || requestType === 'collapsing' || requestType === 'collapse') && !isRemoteData(this.parent)) {
1375
1831
  this.selectedItems = [];
1832
+ this.selectedUidMap = new Map();
1376
1833
  this.selectedIndexes = [];
1834
+ this.refreshVisibleCheckboxes();
1835
+ if (this.parent.autoCheckHierarchy) {
1836
+ this.updateHeaderCheckboxState();
1837
+ }
1377
1838
  }
1378
1839
  }
1379
1840
  }
1380
1841
  }
1381
- getCheckedrecords() {
1382
- return this.selectedItems;
1383
- }
1842
+ /**
1843
+ * Retrieves checked record objects.
1844
+ * This array maintains the `ITreeData` objects in the order they were selected.
1845
+ *
1846
+ * @returns {ITreeData[]} Array of checked records.
1847
+ */
1848
+ getCheckedrecords() { return this.selectedItems; }
1849
+ /**
1850
+ * Retrieves visible indexes of checked rows in the current view, in the order they were selected.
1851
+ * This method dynamically generates the list of visible indexes by iterating through `selectedItems`
1852
+ * (which preserves selection order) and finding their *current* visible index.
1853
+ *
1854
+ * @returns {number[]} Array of checked row indexes in selection order.
1855
+ */
1384
1856
  getCheckedRowIndexes() {
1385
- return this.selectedIndexes;
1857
+ this.buildVisibleUidMap();
1858
+ const orderedVisibleIndexes = [];
1859
+ for (const selectedItem of this.selectedItems) {
1860
+ const uid = selectedItem.uniqueID;
1861
+ if (uid !== undefined && this.visibleUidIndex[uid] !== undefined) {
1862
+ orderedVisibleIndexes.push(this.visibleUidIndex[uid]);
1863
+ }
1864
+ }
1865
+ return orderedVisibleIndexes;
1386
1866
  }
1387
1867
  }
1388
1868
 
@@ -1944,6 +2424,7 @@ class DataManipulation {
1944
2424
  */
1945
2425
  destroy() {
1946
2426
  this.removeEventListener();
2427
+ this.hierarchyData = null;
1947
2428
  }
1948
2429
  /**
1949
2430
  * @hidden
@@ -2544,7 +3025,7 @@ class DataManipulation {
2544
3025
  this.parent.parentData.push(currentData);
2545
3026
  }
2546
3027
  currentData.uniqueID = getUid(this.parent.element.id + '_data_');
2547
- setValue('uniqueIDCollection.' + currentData.uniqueID, currentData, this.parent);
3028
+ this.parent.uniqueIDCollection[currentData.uniqueID] = currentData;
2548
3029
  if (!isNullOrUndefined(parentRecords)) {
2549
3030
  const parentData = extend$1({}, parentRecords);
2550
3031
  delete parentData.childRecords;
@@ -3534,7 +4015,9 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
3534
4015
  constructor(options, element) {
3535
4016
  super(options, element);
3536
4017
  this.dataResults = {};
4018
+ /** @hidden */
3537
4019
  this.uniqueIDCollection = {};
4020
+ /** @hidden */
3538
4021
  this.uniqueIDFilterCollection = {};
3539
4022
  this.changedRecords = 'changedRecords';
3540
4023
  this.deletedRecords = 'deletedRecords';
@@ -3544,6 +4027,8 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
3544
4027
  this.modifiedRecords = [];
3545
4028
  this.stackedHeader = false;
3546
4029
  this.freezeColumnRefresh = true;
4030
+ this.componentRefresh = Component.prototype.refresh;
4031
+ this.isComponentRefresh = false;
3547
4032
  this.objectEqualityChecker = (old, current) => {
3548
4033
  if (old) {
3549
4034
  const keys = Object.keys(old);
@@ -3592,7 +4077,7 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
3592
4077
  /* eslint-disable */
3593
4078
  excelExport(excelExportProperties, isMultipleExport, workbook, isBlob) {
3594
4079
  /* eslint-enable */
3595
- return this.excelExportModule.Map(excelExportProperties, isMultipleExport, workbook, isBlob, false);
4080
+ return this.allowExcelExport ? this.excelExportModule.Map(excelExportProperties, isMultipleExport, workbook, isBlob, false) : null;
3596
4081
  }
3597
4082
  /**
3598
4083
  * Exports the TreeGrid data to a CSV file.
@@ -3606,7 +4091,7 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
3606
4091
  /* eslint-disable */
3607
4092
  csvExport(excelExportProperties, isMultipleExport, workbook, isBlob) {
3608
4093
  /* eslint-enable */
3609
- return this.excelExportModule.Map(excelExportProperties, isMultipleExport, workbook, isBlob, true);
4094
+ return this.allowExcelExport ? this.excelExportModule.Map(excelExportProperties, isMultipleExport, workbook, isBlob, true) : null;
3610
4095
  }
3611
4096
  /**
3612
4097
  * Exports the TreeGrid data to a PDF document.
@@ -3618,7 +4103,7 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
3618
4103
  * @returns {Promise<any>} - Returns a promise that resolves with the result of the export action.
3619
4104
  */
3620
4105
  pdfExport(pdfExportProperties, isMultipleExport, pdfDoc, isBlob) {
3621
- return this.pdfExportModule.Map(pdfExportProperties, isMultipleExport, pdfDoc, isBlob);
4106
+ return this.allowPdfExport ? this.pdfExportModule.Map(pdfExportProperties, isMultipleExport, pdfDoc, isBlob) : null;
3622
4107
  }
3623
4108
  /**
3624
4109
  * Sends a POST request to export the TreeGrid to an Excel file on the server side.
@@ -3738,6 +4223,26 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
3738
4223
  getModuleName() {
3739
4224
  return 'treegrid';
3740
4225
  }
4226
+ /**
4227
+ * Initiates a complete refresh of the TreeGrid's column and layout.
4228
+ *
4229
+ * This method forces a full re-render of the TreeGrid, ensuring that any dynamic
4230
+ * changes to columns or layout are immediately reflected.
4231
+ *
4232
+ * @returns {void}
4233
+ */
4234
+ refreshLayout() {
4235
+ this.componentRefresh();
4236
+ }
4237
+ /**
4238
+ * @param {Object} prop - Defines the property
4239
+ * @param {boolean} muteOnChange - Defines the mute on change
4240
+ * @returns {void}
4241
+ * @private
4242
+ */
4243
+ setProperties(prop, muteOnChange) {
4244
+ super.setProperties(prop, muteOnChange);
4245
+ }
3741
4246
  /**
3742
4247
  * For internal use only - Initialize the event handler;
3743
4248
  *
@@ -3745,6 +4250,9 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
3745
4250
  * @returns {void}
3746
4251
  */
3747
4252
  preRender() {
4253
+ if (this.isComponentRefresh) {
4254
+ this.grid = new Grid();
4255
+ }
3748
4256
  this.TreeGridLocale();
3749
4257
  this.initProperties();
3750
4258
  this.defaultLocale = {
@@ -3914,7 +4422,10 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
3914
4422
  }
3915
4423
  }
3916
4424
  else {
3917
- this.clearSelection();
4425
+ const contentTableBody = this.grid.getContent().querySelector('.e-table tbody');
4426
+ if (parentTarget && contentTableBody && parentTarget !== contentTableBody.lastElementChild) {
4427
+ this.clearSelection();
4428
+ }
3918
4429
  }
3919
4430
  }
3920
4431
  }
@@ -3985,6 +4496,8 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
3985
4496
  this.isExpandAll = false;
3986
4497
  this.isCollapseAll = false;
3987
4498
  this.freezeColumnRefresh = true;
4499
+ this.componentRefresh = Component.prototype.refresh;
4500
+ this.isComponentRefresh = false;
3988
4501
  this.keyConfigs = {
3989
4502
  ctrlDownArrow: 'ctrl+downarrow',
3990
4503
  ctrlUpArrow: 'ctrl+uparrow',
@@ -4337,7 +4850,7 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
4337
4850
  if (RecordsCount === 0 && this.columns.length === 0) {
4338
4851
  failureCases.push('Either of the Data source or columns should be given.');
4339
4852
  }
4340
- if (this.frozenColumns > 0 && this.columnModel.filter((col) => col.isFrozen)) {
4853
+ if (this.frozenColumns > 0 && this.columnModel.filter((col) => col.isFrozen).length > 0) {
4341
4854
  failureCases.push('Use only one attribute for Frozen either IsFrozen or FrozenColumns.');
4342
4855
  }
4343
4856
  if (this.enableVirtualization && !isNullOrUndefined(this.detailTemplate)) {
@@ -4347,12 +4860,21 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
4347
4860
  && (!isNullOrUndefined(this.detailTemplate) || !isNullOrUndefined(this.rowTemplate))) {
4348
4861
  failureCases.push('Frozen rows and columns are not supported with the Detail template and row template.');
4349
4862
  }
4350
- if ((this.frozenColumns > 0 || this.columnModel.filter((col) => col.isFrozen).length > 0 || this.frozenRows > 0) && this.editSettings.mode === 'Cell') {
4863
+ if ((this.frozenColumns > 0 || this.columnModel.filter((col) => col.isFrozen).length > 0 || this.frozenRows > 0) && this.editSettings.allowEditing && this.editSettings.mode === 'Cell') {
4351
4864
  failureCases.push('Frozen rows and columns are not supported with cell editing.');
4352
4865
  }
4353
4866
  if (this.allowSelection && !isNullOrUndefined(this.rowTemplate)) {
4354
4867
  failureCases.push('Selection is not supported in RowTemplate');
4355
4868
  }
4869
+ if (!this.allowExcelExport && this.action === 'csvExport') {
4870
+ failureCases.push('CSV export is not allowed when allowExcelExport is disabled.');
4871
+ }
4872
+ if (!this.allowPdfExport && this.action === 'pdfExport') {
4873
+ failureCases.push('PDF export is not allowed when allowPdfExport is disabled');
4874
+ }
4875
+ if (!this.allowExcelExport && this.action === 'excelExport') {
4876
+ failureCases.push('Excel export is not allowed when allowExcelExport is disabled.');
4877
+ }
4356
4878
  if (this.treeColumnIndex >= this.columns.length) {
4357
4879
  failureCases.push('TreeColumnIndex value should not exceed the total column count.');
4358
4880
  }
@@ -4507,6 +5029,8 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
4507
5029
  this.grid.frozenRows = this.frozenRows;
4508
5030
  this.grid.frozenColumns = this.frozenColumns;
4509
5031
  this.grid.clipMode = getActualProperties(this.clipMode);
5032
+ this.grid.enableColumnSpan = this.enableColumnSpan;
5033
+ this.grid.enableRowSpan = this.enableRowSpan;
4510
5034
  const templateInstance = 'templateDotnetInstance';
4511
5035
  this.grid[`${templateInstance}`] = this[`${templateInstance}`];
4512
5036
  const isJsComponent = 'isJsComponent';
@@ -4981,12 +5505,12 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
4981
5505
  this.grid.refresh();
4982
5506
  }
4983
5507
  if (args.action === 'filter') {
4984
- if (!args.isCollapseMaintain && this.filterModule['currentFilterObject'] !== '' && this.enableVirtualization && !this.initialRender && !(isRemoteData(this) && this.enableVirtualization)) {
5508
+ if (!args.isCollapseMaintain && this.filterModule['currentFilterObject'] !== '' && this.enableVirtualization && !this.initialRender && !this.expandStateMapping && !(isRemoteData(this) && this.enableVirtualization)) {
4985
5509
  this.expandAll();
4986
5510
  }
4987
5511
  }
4988
5512
  if (args.requestType === 'searching') {
4989
- if (!args.isCollapseMaintain && this.searchSettings.key !== '' && this.enableVirtualization && !this.initialRender && !(isRemoteData(this) && this.enableVirtualization)) {
5513
+ if (!args.isCollapseMaintain && this.searchSettings.key !== '' && this.enableVirtualization && !this.initialRender && !this.expandStateMapping && !(isRemoteData(this) && this.enableVirtualization)) {
4990
5514
  this.expandAll();
4991
5515
  }
4992
5516
  }
@@ -5321,11 +5845,14 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
5321
5845
  onPropertyChanged(newProp) {
5322
5846
  const properties = Object.keys(newProp);
5323
5847
  let requireRefresh = false;
5324
- if (properties.indexOf('columns') > -1 && !isNullOrUndefined(newProp.columns)) {
5325
- this.refreshColumns();
5326
- }
5327
5848
  for (const prop of properties) {
5328
5849
  switch (prop) {
5850
+ case 'columns':
5851
+ if (!isNullOrUndefined(newProp.columns)) {
5852
+ this.refreshColumns();
5853
+ }
5854
+ requireRefresh = true;
5855
+ break;
5329
5856
  case 'treeColumnIndex':
5330
5857
  this.grid.refreshColumns();
5331
5858
  break;
@@ -5536,8 +6063,19 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
5536
6063
  }
5537
6064
  this.grid.editSettings = this.getGridEditSettings();
5538
6065
  break;
6066
+ case 'enableRowSpan':
6067
+ case 'enableColumnSpan':
6068
+ this.grid.enableRowSpan = this.enableRowSpan;
6069
+ this.grid.enableColumnSpan = this.enableColumnSpan;
6070
+ this.refreshColumns();
6071
+ break;
6072
+ }
6073
+ }
6074
+ if (requireRefresh) {
6075
+ if (this.isFrozenGrid()) {
6076
+ this.refreshLayout();
5539
6077
  }
5540
- if (requireRefresh) {
6078
+ else {
5541
6079
  this.grid.refresh();
5542
6080
  }
5543
6081
  }
@@ -5558,6 +6096,7 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
5558
6096
  * @returns {void}
5559
6097
  */
5560
6098
  destroy() {
6099
+ this.isComponentRefresh = true;
5561
6100
  const treeGridElement = this.element;
5562
6101
  if (!treeGridElement) {
5563
6102
  return;
@@ -5568,15 +6107,20 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
5568
6107
  this.unwireEvents();
5569
6108
  }
5570
6109
  this.removeListener();
5571
- if (hasTreeGridChild) {
5572
- super.destroy();
6110
+ if (this.dataModule) {
6111
+ this.dataModule.destroy();
5573
6112
  }
5574
6113
  if (this.grid) {
6114
+ this.grid.dataSource = null;
5575
6115
  this.grid.destroy();
5576
6116
  }
5577
- if (this.dataModule) {
5578
- this.dataModule.destroy();
6117
+ if (hasTreeGridChild) {
6118
+ super.destroy();
5579
6119
  }
6120
+ this.infiniteScrollData = null;
6121
+ this.remoteCollapsedData = null;
6122
+ this.remoteExpandedData = null;
6123
+ this.parentData = null;
5580
6124
  const modules = ['dataModule', 'sortModule', 'renderModule', 'filterModule', 'printModule', 'clipboardModule',
5581
6125
  'excelExportModule', 'pdfExportModule', 'toolbarModule', 'summaryModule', 'reorderModule', 'resizeModule',
5582
6126
  'pagerModule', 'keyboardModule', 'columnMenuModule', 'contextMenuModule', 'editModule', 'virtualScrollModule',
@@ -5586,6 +6130,9 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
5586
6130
  this[modules[parseInt(i.toString(), 10)]] = null;
5587
6131
  }
5588
6132
  }
6133
+ this.dataResults = null;
6134
+ this.uniqueIDCollection = {};
6135
+ this.uniqueIDFilterCollection = {};
5589
6136
  this.element.innerHTML = '';
5590
6137
  this.grid = null;
5591
6138
  }
@@ -5726,7 +6273,7 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
5726
6273
  /**
5727
6274
  * Adds a new record to the TreeGrid at the specified position or default location.
5728
6275
  *
5729
- * @param {Object} data - Object containing the data for the new record. If omitted, an empty row is added.
6276
+ * @param {Object | Object[]} data - Object containing data for a single record, or an array of objects for creating multiple records. If omitted, an empty row is added.
5730
6277
  * @param {number} index - The index at which the new row should be added.
5731
6278
  * @param {RowPosition} position - Specifies the position of the new row (e.g., before, after or child).
5732
6279
  *
@@ -6535,7 +7082,9 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
6535
7082
  if (isNullOrUndefined(refreshUI) || refreshUI) {
6536
7083
  this.grid.columns = this.getGridColumns(this.columns);
6537
7084
  this.getTreeColumn();
6538
- this.grid.refreshColumns();
7085
+ if (!this.isFrozenGrid()) {
7086
+ this.grid.refreshColumns();
7087
+ }
6539
7088
  }
6540
7089
  else {
6541
7090
  this.grid.setProperties({ columns: this.getGridColumns(this.columns) }, true);
@@ -7067,7 +7616,8 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
7067
7616
  if (rows.length) {
7068
7617
  for (let i = 0; i < rows.length; i++) {
7069
7618
  if (action === 'collapse') {
7070
- if (!isNullOrUndefined(this.getCurrentViewRecords()[rows[parseInt(i.toString(), 10)].rowIndex])) {
7619
+ const currentRecordIndx = this.frozenRows ? this.getCurrentViewRecords()[parseInt(rows[parseInt(i.toString(), 10)].getAttribute('aria-rowindex'), 10) - 1] : this.getCurrentViewRecords()[rows[parseInt(i.toString(), 10)].rowIndex];
7620
+ if (!isNullOrUndefined(currentRecordIndx)) {
7071
7621
  this.collapseRow(rows[parseInt(i.toString(), 10)]);
7072
7622
  }
7073
7623
  }
@@ -7191,8 +7741,9 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
7191
7741
  }
7192
7742
  const lastrowIdx = this.getVisibleRecords()[this.getVisibleRecords().length - 1]['index'];
7193
7743
  const lastRow = this.getRowByIndex(lastrowIdx);
7194
- if (this.grid.getContentTable().clientHeight <= this.grid.getContent().clientHeight && !isNullOrUndefined(lastRow) && !lastRow.cells[0].classList.contains('e-lastrowcell')) {
7195
- this.lastRowBorder(lastRow, true);
7744
+ const borderElement = lastRow ? lastRow.nextElementSibling ? lastRow.nextElementSibling.classList.contains('e-detailrow') ? lastRow.nextElementSibling : lastRow : lastRow : null;
7745
+ if (this.grid.getContentTable().clientHeight <= this.grid.getContent().clientHeight && !isNullOrUndefined(borderElement) && !borderElement.cells[0].classList.contains('e-lastrowcell')) {
7746
+ this.lastRowBorder(borderElement, true);
7196
7747
  }
7197
7748
  }
7198
7749
  if (isCountRequired(this) && action === 'expand') {
@@ -7353,12 +7904,16 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
7353
7904
  }
7354
7905
  localExpand(action, row, record) {
7355
7906
  let rows;
7907
+ const detailRow = row.nextElementSibling ? row.nextElementSibling.classList.contains('e-detailrow') ? row.nextElementSibling : null : null;
7356
7908
  const childRecords = this.grid.currentViewData.filter((e) => {
7357
7909
  return e.parentUniqueID === record.uniqueID;
7358
7910
  });
7359
7911
  if (this.isPixelHeight() && row.cells[0].classList.contains('e-lastrowcell')) {
7360
7912
  this.lastRowBorder(row, false);
7361
7913
  }
7914
+ else if (this.isPixelHeight() && detailRow && detailRow.cells[0].classList.contains('e-lastrowcell')) {
7915
+ this.lastRowBorder(row.nextElementSibling, false);
7916
+ }
7362
7917
  let movableRows;
7363
7918
  let freezeRightRows;
7364
7919
  let gridRows = this.getRows();
@@ -7561,6 +8116,7 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
7561
8116
  this.on('updateResults', this.updateResultModel, this);
7562
8117
  this.grid.on('initial-end', this.afterGridRender, this);
7563
8118
  this.grid.on('partial-filter-update', this.partialFilterUpdate, this);
8119
+ this.grid.on('get-row-cells', this.getCellsByTableName, this);
7564
8120
  }
7565
8121
  updateResultModel(returnResult) {
7566
8122
  this.dataResults = returnResult;
@@ -7577,6 +8133,16 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
7577
8133
  this.grid.off('initial-end', this.afterGridRender);
7578
8134
  this.grid.off('last-rowcell-border-updated', this.lastRowCellBorderUpdated);
7579
8135
  this.grid.off('partial-filter-update', this.partialFilterUpdate);
8136
+ this.grid.off('get-row-cells', this.getCellsByTableName);
8137
+ }
8138
+ getCellsByTableName(args) {
8139
+ if (!Array.isArray(args.elements)) {
8140
+ args.elements = [];
8141
+ }
8142
+ if (args.rowIndex < this.grid.getDataRows().length) {
8143
+ const cells = [].slice.call(this.grid.getDataRows()[parseInt(args.rowIndex.toString(), 10)].getElementsByClassName('e-rowcell'));
8144
+ Array.prototype.push.apply(args.elements, cells);
8145
+ }
7580
8146
  }
7581
8147
  partialFilterUpdate(args) {
7582
8148
  const gridFiltered = args.gridFiltered;
@@ -8001,6 +8567,53 @@ let TreeGrid = TreeGrid_1 = class TreeGrid extends Component {
8001
8567
  this.rowDragAndDropModule[this.indentOutdentAction](record, 'outdent');
8002
8568
  }
8003
8569
  }
8570
+ /**
8571
+ * Calculates and returns the optimal page size that fits the current height of the TreeGrid's container.
8572
+ *
8573
+ * @param {number | string } containerHeight - (Optional) The height of the container - i.e. the complete TreeGrid height, which can be a number (in pixels) or a string.
8574
+ * @returns {number} returns the page size
8575
+ */
8576
+ getPageSizeByHeight(containerHeight) {
8577
+ if (isNullOrUndefined(containerHeight)) {
8578
+ const treegridControlElement = document.getElementById(this.element.id);
8579
+ if (treegridControlElement) {
8580
+ containerHeight = treegridControlElement.clientHeight;
8581
+ }
8582
+ else {
8583
+ const root = this.element;
8584
+ containerHeight = root ? (root.offsetHeight || root.clientHeight || 0) : 0;
8585
+ }
8586
+ }
8587
+ if ((this.allowTextWrap && this.textWrapSettings.wrapMode === 'Header') || (!this.allowTextWrap)) {
8588
+ let pagesize = 0;
8589
+ if (typeof containerHeight === 'string' && containerHeight.indexOf('%') !== -1) {
8590
+ containerHeight = parseInt(containerHeight, 10) / 100 * this.element.clientHeight;
8591
+ }
8592
+ const nonContentHeight = this.grid['getNoncontentHeight']() + this.grid.getRowHeight();
8593
+ if (containerHeight > nonContentHeight) {
8594
+ let contentHeight = 0;
8595
+ let calcNonContentHeight = this.grid['getNoncontentHeight']();
8596
+ const pagerMsg = document.getElementsByClassName('e-pagerexternalmsg')[0];
8597
+ if (pagerMsg) {
8598
+ calcNonContentHeight += pagerMsg.clientHeight;
8599
+ }
8600
+ contentHeight = containerHeight - calcNonContentHeight;
8601
+ pagesize = (contentHeight / this.grid.getRowHeight());
8602
+ }
8603
+ if (this.frozenRows > 0) {
8604
+ pagesize = pagesize + this.frozenRows;
8605
+ }
8606
+ if (pagesize > 0) {
8607
+ return Math.floor(pagesize);
8608
+ }
8609
+ else {
8610
+ return 0;
8611
+ }
8612
+ }
8613
+ else {
8614
+ return 0;
8615
+ }
8616
+ }
8004
8617
  };
8005
8618
  __decorate$c([
8006
8619
  Property(0)
@@ -8215,6 +8828,12 @@ __decorate$c([
8215
8828
  __decorate$c([
8216
8829
  Property(false)
8217
8830
  ], TreeGrid.prototype, "allowPdfExport", void 0);
8831
+ __decorate$c([
8832
+ Property(false)
8833
+ ], TreeGrid.prototype, "enableColumnSpan", void 0);
8834
+ __decorate$c([
8835
+ Property(false)
8836
+ ], TreeGrid.prototype, "enableRowSpan", void 0);
8218
8837
  __decorate$c([
8219
8838
  Event()
8220
8839
  ], TreeGrid.prototype, "created", void 0);
@@ -8716,6 +9335,9 @@ class RowDD {
8716
9335
  this.selectedItem = isNullOrUndefined(record) ?
8717
9336
  tObj.getCurrentViewRecords()[parseInt(selectedItemIndex.toString(), 10)] : record;
8718
9337
  const primaryKeyField = this.parent.getPrimaryKeyFieldNames()[0];
9338
+ if (!primaryKeyField) {
9339
+ return;
9340
+ }
8719
9341
  const rowIndex = this.parent.grid.getRowIndexByPrimaryKey(this.selectedItem[`${primaryKeyField}`]);
8720
9342
  this.selectedRow = this.parent[this.selectedRows] = selectedItemIndex !== -1 ?
8721
9343
  this.parent.getSelectedRows()[0]
@@ -11653,7 +12275,7 @@ class Aggregate {
11653
12275
  const value = types[parseInt(i.toString(), 10)] !== 'Custom' ? val[`${key}`] : val;
11654
12276
  single[`${disp}`] = single[`${disp}`] || {};
11655
12277
  single[`${disp}`][`${key}`] = value;
11656
- single[`${disp}`][types[parseInt(i.toString(), 10)]] = !isNullOrUndefined(val) ? formatFn(value) : ' ';
12278
+ single[`${disp}`][types[parseInt(i.toString(), 10)]] = (!isNullOrUndefined(val) && !isNullOrUndefined(value)) ? formatFn(value) : ' ';
11657
12279
  }
11658
12280
  helper.format = summaryColumn.getFormatter();
11659
12281
  const cellElement = createElement('td', {
@@ -11877,6 +12499,7 @@ class ContextMenu {
11877
12499
  addEventListener() {
11878
12500
  this.parent.on('contextMenuOpen', this.contextMenuOpen, this);
11879
12501
  this.parent.on('contextMenuClick', this.contextMenuClick, this);
12502
+ this.parent.on('contextMenuItemClick', this.contextMenuItemClick, this);
11880
12503
  }
11881
12504
  /**
11882
12505
  * @hidden
@@ -11888,6 +12511,21 @@ class ContextMenu {
11888
12511
  }
11889
12512
  this.parent.off('contextMenuOpen', this.contextMenuOpen);
11890
12513
  this.parent.off('contextMenuClick', this.contextMenuClick);
12514
+ this.parent.off('contextMenuItemClick', this.contextMenuItemClick);
12515
+ }
12516
+ contextMenuItemClick(args) {
12517
+ const id = args.item && args.item.id ? args.item.id : '';
12518
+ const delId = this.parent.element.id + '_gridcontrol_cmenu_Delete';
12519
+ if (id !== delId) {
12520
+ return;
12521
+ }
12522
+ if (this.parent.getSelectedRecords()[0].hasChildRecords || this.parent.getSelectedRecords().length > 1) {
12523
+ this.parent.deleteRecord();
12524
+ }
12525
+ else {
12526
+ this.parent.deleteRow(this.parent.getSelectedRows()[0]);
12527
+ }
12528
+ args.cancel = true;
11891
12529
  }
11892
12530
  contextMenuOpen(args) {
11893
12531
  const addRow = select('#' + this.parent.element.id + '_gridcontrol_cmenu_AddRow', args.element);
@@ -12269,13 +12907,22 @@ class BatchEdit {
12269
12907
  focusModule.getContent().matrix.matrix = this.matrix;
12270
12908
  }
12271
12909
  else {
12272
- actualIndex = table.getElementsByClassName('e-batchrow')[0].rowIndex;
12910
+ if (this.parent.frozenRows) {
12911
+ actualIndex = this.batchIndex;
12912
+ }
12913
+ else if (this.parent.editModule.isAddedMultipleRowsByMethod) {
12914
+ actualIndex = e.index;
12915
+ }
12916
+ else {
12917
+ actualIndex = table.getElementsByClassName('e-batchrow')[0].rowIndex;
12918
+ }
12273
12919
  // if (this.parent.frozenRows || this.parent.frozenColumns) {
12274
12920
  // actualIndex = this.batchIndex;
12275
12921
  // }
12276
12922
  }
12277
12923
  focusModule.getContent().matrix.current = [actualIndex, focusModule.getContent().matrix.current[1]];
12278
- if (this.parent.editModule['isAddedRowByMethod'] && !isNullOrUndefined(this.parent.editModule['addRowIndex']) && !this.parent.editModule['isAddedRowByContextMenu']) {
12924
+ if (this.parent.editModule['isAddedRowByMethod'] && !isNullOrUndefined(this.parent.editModule['addRowIndex']) &&
12925
+ !this.parent.editModule['isAddedRowByContextMenu'] && !this.parent.editModule.isAddedMultipleRowsByMethod) {
12279
12926
  const newlyAddedRecords = this.parent.getBatchChanges()['addedRecords'];
12280
12927
  const index = parseInt(this.parent.getContentTable().getElementsByClassName('e-insertedrow')[newlyAddedRecords.length - 1].getAttribute('aria-rowindex'), 10) - 1;
12281
12928
  this.batchRecords.splice(index, 0, newlyAddedRecords[newlyAddedRecords.length - 1]);
@@ -12515,7 +13162,12 @@ class BatchEdit {
12515
13162
  this.parent.editModule['previousNewRowPosition'] = rowPosition;
12516
13163
  }
12517
13164
  addRecords[parseInt(i.toString(), 10)].taskData = taskData;
12518
- addRowRecord = this.batchAddRowRecord[parseInt(i.toString(), 10)];
13165
+ if (this.batchAddRowRecord.length > 1) {
13166
+ addRowRecord = this.batchAddRowRecord[parseInt(i.toString(), 10)];
13167
+ }
13168
+ else {
13169
+ addRowRecord = this.batchAddRowRecord[0];
13170
+ }
12519
13171
  if (isNullOrUndefined(addRowRecord)) {
12520
13172
  addRowRecord = this.batchAddRowRecord[i - 1];
12521
13173
  }
@@ -12530,6 +13182,10 @@ class BatchEdit {
12530
13182
  if (isNullOrUndefined(addRecords[parseInt(i.toString(), 10)].index)) {
12531
13183
  addRowIndex = 0;
12532
13184
  }
13185
+ if (this.parent.editModule.isAddedMultipleRowsByMethod && this.isSelfReference && (this.parent.editSettings.newRowPosition === 'Above' || this.parent.editSettings.newRowPosition === 'Below')) {
13186
+ addRowIndex = args.index;
13187
+ addRowRecord = this.parent.flatData[args.index];
13188
+ }
12533
13189
  if (this.parent.editSettings.newRowPosition !== 'Top' && this.parent.editSettings.newRowPosition !== 'Bottom') {
12534
13190
  if (isNullOrUndefined(addRecords[parseInt(i.toString(), 10)].parentItem) && this.selectedIndex === -1) {
12535
13191
  selectedIndex = -1;
@@ -12656,6 +13312,7 @@ class Edit {
12656
13312
  this.isAddedRowByMethod = false;
12657
13313
  this.isAddedRowByContextMenu = false;
12658
13314
  this.isIndexUndefined = false;
13315
+ this.isAddedMultipleRowsByMethod = false;
12659
13316
  Grid.Inject(Edit$1);
12660
13317
  this.parent = parent;
12661
13318
  this.isSelfReference = !isNullOrUndefined(parent.parentIdMapping);
@@ -13312,7 +13969,8 @@ class Edit {
13312
13969
  }
13313
13970
  }
13314
13971
  }
13315
- if (this.parent.editSettings.mode === 'Batch' && !isNullOrUndefined(this.addRowIndex) && this.addRowIndex !== -1 && this['isAddedRowByMethod'] && !this.isAddedRowByContextMenu) {
13972
+ if (this.parent.editSettings.mode === 'Batch' && !isNullOrUndefined(this.addRowIndex) && this.addRowIndex !== -1 &&
13973
+ !this.isAddedMultipleRowsByMethod && this['isAddedRowByMethod'] && !this.isAddedRowByContextMenu) {
13316
13974
  index = this.batchEditModule.getAddRowIndex();
13317
13975
  this.selectedIndex = this.batchEditModule.getSelectedIndex();
13318
13976
  const batchAddedRecords = this.parent.getBatchChanges()['addedRecords'];
@@ -13334,6 +13992,28 @@ class Edit {
13334
13992
  this.batchEditModule['batchAddRowRecord'].push(this.batchEditModule['addRowRecord']);
13335
13993
  this.batchEditModule['batchAddedRecords'].push(args['data']);
13336
13994
  }
13995
+ else if (this.parent.editSettings.mode === 'Batch' && this.isAddedMultipleRowsByMethod && (this.parent.editSettings.newRowPosition === 'Above' || this.parent.editSettings.newRowPosition === 'Below')) {
13996
+ index = this.multipleRowIndex;
13997
+ this.selectedIndex = this.multipleRowIndex;
13998
+ const batchAddedRecords = this.updatedRecords.addedRecords;
13999
+ let newlyAddedRecord;
14000
+ if (batchAddedRecords.length) {
14001
+ for (let i = 0; i < batchAddedRecords.length; i++) {
14002
+ if (isNullOrUndefined(batchAddedRecords[parseInt(i.toString(), 10)].uniqueID)) {
14003
+ newlyAddedRecord = batchAddedRecords[parseInt(i.toString(), 10)];
14004
+ }
14005
+ const args = {
14006
+ action: 'add',
14007
+ data: newlyAddedRecord,
14008
+ index: index,
14009
+ seletedRow: 0
14010
+ };
14011
+ this.beginAddEdit(args);
14012
+ this.batchEditModule['batchAddRowRecord'].push(this.batchEditModule['addRowRecord']);
14013
+ this.batchEditModule['batchAddedRecords'].push(args['data']);
14014
+ }
14015
+ }
14016
+ }
13337
14017
  }
13338
14018
  // private beforeDataBound(args: BeforeDataBoundArgs): void {
13339
14019
  // if (this.parent.grid.isEdit && this.parent.dataSource instanceof DataManager &&
@@ -13618,7 +14298,35 @@ class Edit {
13618
14298
  if (isNullOrUndefined(index)) {
13619
14299
  this.isIndexUndefined = true;
13620
14300
  }
13621
- if (!this.isSelfReference && !isNullOrUndefined(data) && Object.hasOwnProperty.call(data, this.parent.childMapping)) {
14301
+ if (!isNullOrUndefined(data) && Array.isArray(data)) {
14302
+ let addRecords = [];
14303
+ const previousEditMode = this.parent.editSettings.mode;
14304
+ const previousGridEditMode = this.parent.grid.editSettings.mode;
14305
+ if (!this.isSelfReference && !isNullOrUndefined(data) && Object.hasOwnProperty.call(data, this.parent.childMapping)) {
14306
+ addRecords.push(data);
14307
+ }
14308
+ else if (Array.isArray(data)) {
14309
+ addRecords = data;
14310
+ }
14311
+ this.parent.setProperties({ editSettings: { mode: 'Batch' } }, true);
14312
+ this.parent.grid.setProperties({ editSettings: { mode: 'Batch' } }, true);
14313
+ if (!isNullOrUndefined(position)) {
14314
+ this.parent.setProperties({ editSettings: { newRowPosition: position } }, true);
14315
+ }
14316
+ this.updatedRecords = { addedRecords: addRecords, changedRecords: [], deletedRecords: [] };
14317
+ if ((position === 'Above' || position === 'Below') && this.isSelfReference) {
14318
+ this.isAddedMultipleRowsByMethod = true;
14319
+ this.multipleRowIndex = index;
14320
+ this.addRowIndex = index;
14321
+ this.parent.notify(batchAdd, { updatedRecords: this.updatedRecords, index: index });
14322
+ }
14323
+ const updatedRecords = this.updatedRecords;
14324
+ this.parent.notify(batchSave, { updatedRecords, index });
14325
+ this.parent.setProperties({ editSettings: { mode: previousEditMode } }, true);
14326
+ this.parent.grid.setProperties({ editSettings: { mode: previousGridEditMode } }, true);
14327
+ this.parent.refresh();
14328
+ }
14329
+ else if (!this.isSelfReference && !isNullOrUndefined(data) && Object.hasOwnProperty.call(data, this.parent.childMapping)) {
13622
14330
  const addRecords = [];
13623
14331
  const previousEditMode = this.parent.editSettings.mode;
13624
14332
  const previousGridEditMode = this.parent.grid.editSettings.mode;
@@ -13628,7 +14336,8 @@ class Edit {
13628
14336
  if (!isNullOrUndefined(position)) {
13629
14337
  this.parent.setProperties({ editSettings: { newRowPosition: position } }, true);
13630
14338
  }
13631
- const updatedRecords = { addedRecords: addRecords, changedRecords: [], deletedRecords: [] };
14339
+ this.updatedRecords = { addedRecords: addRecords, changedRecords: [], deletedRecords: [] };
14340
+ const updatedRecords = this.updatedRecords;
13632
14341
  this.parent.notify(batchSave, { updatedRecords, index });
13633
14342
  this.parent.setProperties({ editSettings: { mode: previousEditMode } }, true);
13634
14343
  this.parent.grid.setProperties({ editSettings: { mode: previousGridEditMode } }, true);
@@ -13916,7 +14625,8 @@ class VirtualTreeContentRenderer extends VirtualContentRenderer {
13916
14625
  */
13917
14626
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
13918
14627
  getRowCollection(index, isMovable, isRowObject, isFrozenRight) {
13919
- const startIdx = parseInt(this.parent.getRows()[0].getAttribute(ariaRowIndex), 10) - 1;
14628
+ const rows = this.parent.getRows();
14629
+ const startIdx = rows.length > 0 ? parseInt(rows[0].getAttribute(ariaRowIndex), 10) - 1 : 0;
13920
14630
  const rowCollection = this.parent.getDataRows();
13921
14631
  const collection = isRowObject ? this.parent.getCurrentViewRecords() : rowCollection;
13922
14632
  let selectedRow = collection[index - startIdx];
@@ -14108,6 +14818,9 @@ class VirtualTreeContentRenderer extends VirtualContentRenderer {
14108
14818
  super.renderTable();
14109
14819
  if (!(this.parent.dataSource instanceof DataManager && this.parent.dataSource.dataSource.url !== undefined
14110
14820
  && this.parent.dataSource.dataSource.offline && this.parent.dataSource.dataSource.url !== '') || !isCountRequired(this.parent)) {
14821
+ if (this.observers) {
14822
+ this.observers.disconnect();
14823
+ }
14111
14824
  getValue('observer', this).options.debounceEvent = false;
14112
14825
  this.observers = new TreeInterSectionObserver(getValue('observer', this).element, getValue('observer', this).options);
14113
14826
  this.contents = this.getPanel().firstChild;
@@ -14822,6 +15535,9 @@ class VirtualTreeContentRenderer extends VirtualContentRenderer {
14822
15535
  this.parent.off('refresh-virtual-editform-cells', this.refreshCell);
14823
15536
  this.parent.off('virtaul-cell-focus', this.cellFocus);
14824
15537
  this.parent.off('virtual-scroll-edit', this.restoreEditState);
15538
+ if (this.observers) {
15539
+ this.observers.disconnect();
15540
+ }
14825
15541
  }
14826
15542
  }
14827
15543
  class TreeInterSectionObserver extends InterSectionObserver {
@@ -14844,12 +15560,28 @@ class TreeInterSectionObserver extends InterSectionObserver {
14844
15560
  observes(callback, onEnterCallback, instance) {
14845
15561
  const containerRect = 'containerRect';
14846
15562
  super[`${containerRect}`] = getValue('options', this).container.getBoundingClientRect();
14847
- EventHandler.add(getValue('options', this).container, 'scroll', this.virtualScrollHandlers(callback, onEnterCallback, instance), this);
15563
+ this.containerEl = getValue('options', this).container;
15564
+ this.containerScrollHandler = this.virtualScrollHandlers(callback, onEnterCallback, instance);
15565
+ EventHandler.add(this.containerEl, 'scroll', this.containerScrollHandler, this);
14848
15566
  if (getValue('options', this).movableContainer) {
14849
15567
  const movableContainerRect = 'movableContainerRect';
14850
15568
  super[`${movableContainerRect}`] = getValue('options', this).movableContainer.getBoundingClientRect();
14851
- EventHandler.add(getValue('options', this).movableContainer, 'scroll', this.virtualScrollHandlers(callback, onEnterCallback, instance), this);
15569
+ this.movableContainerEl = getValue('options', this).movableContainer;
15570
+ this.movableScrollHandler = this.virtualScrollHandlers(callback, onEnterCallback, instance);
15571
+ EventHandler.add(this.movableContainerEl, 'scroll', this.movableScrollHandler, this);
15572
+ }
15573
+ }
15574
+ disconnect() {
15575
+ if (this.containerEl && this.containerScrollHandler) {
15576
+ EventHandler.remove(this.containerEl, 'scroll', this.containerScrollHandler);
15577
+ this.containerScrollHandler = null;
15578
+ }
15579
+ if (this.movableContainerEl && this.movableScrollHandler) {
15580
+ EventHandler.remove(this.movableContainerEl, 'scroll', this.movableScrollHandler);
15581
+ this.movableScrollHandler = null;
14852
15582
  }
15583
+ this.containerEl = null;
15584
+ this.movableContainerEl = null;
14853
15585
  }
14854
15586
  /**
14855
15587
  * Clears the last known position.
@@ -15057,9 +15789,12 @@ class VirtualScroll {
15057
15789
  const dm = new DataManager(pageingDetails.result);
15058
15790
  const expanded = new Predicate$1('expanded', 'notequal', null).or('expanded', 'notequal', undefined);
15059
15791
  const parents = dm.executeLocal(new Query().where(expanded));
15060
- const visualData = parents.filter((e) => {
15061
- return getExpandStatus(this.parent, e);
15062
- });
15792
+ const isFiltering = pageingDetails.actionArgs.requestType === 'filtering';
15793
+ const isFlatHierarchy = this.parent.filterSettings.hierarchyMode === 'Child' ||
15794
+ this.parent.filterSettings.hierarchyMode === 'None';
15795
+ const visualData = isFiltering && isFlatHierarchy
15796
+ ? parents
15797
+ : parents.filter((e) => getExpandStatus(this.parent, e));
15063
15798
  this.visualData = visualData;
15064
15799
  pageingDetails.count = visualData.length;
15065
15800
  this.parent.grid.notify(dataListener, { data: visualData });
@@ -15290,7 +16025,7 @@ class Freeze {
15290
16025
  }
15291
16026
  const queryselector = args.action === 'e-childrow-hidden' ? '.e-treecolumn-container .e-treegridcollapse'
15292
16027
  : '.e-treecolumn-container .e-treegridexpand';
15293
- if (frozenrows[row.rowIndex].querySelector(queryselector)) {
16028
+ if (frozenrows[parseInt(row.getAttribute('aria-rowindex'), 10) - 1].querySelector(queryselector)) {
15294
16029
  const cRow = [];
15295
16030
  for (let i = 0; i < movableRows.length; i++) {
15296
16031
  if (movableRows[parseInt(i.toString(), 10)].querySelector('.e-gridrowindex' + rData.index + 'level' + (rData.level + 1))) {