@wavemaker/app-ng-runtime 11.15.4-rc.250 → 11.15.5-1.256

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 (53) hide show
  1. package/components/base/bundles/index.umd.js +4 -13
  2. package/components/base/esm2022/utils/widget-utils.mjs +5 -4
  3. package/components/base/esm2022/widgets/common/base/base.component.mjs +1 -11
  4. package/components/base/fesm2022/index.mjs +4 -13
  5. package/components/base/fesm2022/index.mjs.map +1 -1
  6. package/components/containers/tabs/bundles/index.umd.js +1 -1
  7. package/components/containers/tabs/esm2022/tabs.animator.mjs +2 -2
  8. package/components/containers/tabs/fesm2022/index.mjs +1 -1
  9. package/components/containers/tabs/fesm2022/index.mjs.map +1 -1
  10. package/components/data/form/bundles/index.umd.js +6 -3
  11. package/components/data/form/esm2022/form.component.mjs +5 -2
  12. package/components/data/form/esm2022/live-form/live-form.directive.mjs +3 -3
  13. package/components/data/form/fesm2022/index.mjs +6 -3
  14. package/components/data/form/fesm2022/index.mjs.map +1 -1
  15. package/components/data/form/live-form/live-form.directive.d.ts +1 -1
  16. package/components/data/list/bundles/index.umd.js +6 -0
  17. package/components/data/list/esm2022/list.component.mjs +7 -1
  18. package/components/data/list/fesm2022/index.mjs +6 -0
  19. package/components/data/list/fesm2022/index.mjs.map +1 -1
  20. package/components/data/pagination/bundles/index.umd.js +45 -7
  21. package/components/data/pagination/esm2022/pagination.component.mjs +46 -8
  22. package/components/data/pagination/fesm2022/index.mjs +45 -7
  23. package/components/data/pagination/fesm2022/index.mjs.map +1 -1
  24. package/components/data/pagination/pagination.component.d.ts +1 -0
  25. package/components/data/table/bundles/index.umd.js +228 -20
  26. package/components/data/table/esm2022/table-column/table-column.directive.mjs +6 -1
  27. package/components/data/table/esm2022/table-cud.directive.mjs +14 -3
  28. package/components/data/table/esm2022/table-filter.directive.mjs +6 -1
  29. package/components/data/table/esm2022/table.component.mjs +206 -19
  30. package/components/data/table/fesm2022/index.mjs +229 -21
  31. package/components/data/table/fesm2022/index.mjs.map +1 -1
  32. package/components/data/table/table.component.d.ts +16 -1
  33. package/core/bundles/index.umd.js +7 -1
  34. package/core/esm2022/utils/dom.mjs +4 -2
  35. package/core/esm2022/utils/utils.mjs +5 -1
  36. package/core/fesm2022/index.mjs +7 -1
  37. package/core/fesm2022/index.mjs.map +1 -1
  38. package/npm-shrinkwrap.json +2 -2
  39. package/package-lock.json +2 -2
  40. package/package.json +1 -1
  41. package/runtime/base/bundles/index.umd.js +45 -12
  42. package/runtime/base/components/app-component/app.component.d.ts +1 -1
  43. package/runtime/base/esm2022/components/app-component/app.component.mjs +23 -12
  44. package/runtime/base/esm2022/guards/role.guard.mjs +2 -2
  45. package/runtime/base/esm2022/services/app.manager.service.mjs +23 -1
  46. package/runtime/base/fesm2022/index.mjs +45 -12
  47. package/runtime/base/fesm2022/index.mjs.map +1 -1
  48. package/runtime/base/services/app.manager.service.d.ts +5 -0
  49. package/scripts/datatable/datatable.js +282 -44
  50. package/transpiler/bundles/index.umd.js +3 -0
  51. package/transpiler/esm2022/build.mjs +4 -1
  52. package/transpiler/fesm2022/index.mjs +3 -0
  53. package/transpiler/fesm2022/index.mjs.map +1 -1
@@ -91,6 +91,11 @@ export declare class AppManagerService implements OnDestroy {
91
91
  getAppLocale(): any;
92
92
  getSelectedLocale(): string;
93
93
  notifyApp(template: any, type: any, title?: any): void;
94
+ /**
95
+ * Notifies access denied for a page (e.g. RoleGuard) and invokes App.onServiceError
96
+ * with a 403-shaped response.
97
+ */
98
+ notifyAccessDenied(pageName?: string, message?: string): void;
94
99
  /**
95
100
  * Triggers the onBeforeService method defined in app.js of the app
96
101
  * @param requestParams
@@ -736,6 +736,10 @@ $.widget('wm.datatable', {
736
736
  colExpression = colDef.customExpression,
737
737
  styles = "text-align: " + colDef.textAlignment + ";position: relative;"
738
738
 
739
+ if (!_.isUndefined(colDef.show) && !colDef.show) {
740
+ styles += "display: none;";
741
+ }
742
+
739
743
  $htm = $('<td class="' + classes + '" data-col-id="' + colId + '" role="cell" tabindex="0"></td>');
740
744
  this._setStyles($htm, styles);
741
745
 
@@ -1131,6 +1135,144 @@ $.widget('wm.datatable', {
1131
1135
  this._setGridEditMode(false);
1132
1136
  this.toggleNewRowActions(false);
1133
1137
  },
1138
+ /*
1139
+ * Updates internal state (options.data, preparedData, custom expressions,
1140
+ * conditional class watchers, and summary row) without a full DOM re-render.
1141
+ * Used during quickedit inline save for model variables to avoid focus loss.
1142
+ */
1143
+ softRefreshData: function (newData) {
1144
+ // With pagination, navigator / selection can emit the same page twice in one
1145
+ // save cycle. Full refresh skipped duplicate data via isEqual on option set;
1146
+ // soft refresh must do the same or onDataRender (datarender) fires twice.
1147
+ if (this.options.data && newData && _.isEqual(this.options.data, newData)) {
1148
+ return;
1149
+ }
1150
+
1151
+ var self = this,
1152
+ isScrollOrOnDemand = this.options.isNavTypeScrollOrOndemand(),
1153
+ pageStartIndex = this.getPageStartIndex(),
1154
+ startRowIndex = this.options.startRowIndex || 0,
1155
+ selectedMap = {},
1156
+ preparedData;
1157
+
1158
+ // Preserve _selected state before _prepareData rebuilds preparedData
1159
+ this.preparedData.forEach(function (data, i) {
1160
+ if (data._selected) {
1161
+ selectedMap[i] = true;
1162
+ }
1163
+ });
1164
+
1165
+ this.options.data = newData;
1166
+ this._prepareData();
1167
+
1168
+ // Restore _selected state on new preparedData
1169
+ Object.keys(selectedMap).forEach(function (i) {
1170
+ if (self.preparedData[i]) {
1171
+ self.preparedData[i]._selected = true;
1172
+ }
1173
+ });
1174
+
1175
+ if (isScrollOrOnDemand) {
1176
+ preparedData = this.preparedData.slice(pageStartIndex);
1177
+ } else {
1178
+ preparedData = this.preparedData;
1179
+ }
1180
+
1181
+ // Update existing custom expression view contexts in-place
1182
+ // so Angular change detection picks up new data without DOM manipulation.
1183
+ if (_.isFunction(this.options.updateCustomExpressionContexts)) {
1184
+ this.options.updateCustomExpressionContexts(preparedData);
1185
+ }
1186
+
1187
+ // Update plain text cells with fresh data from preparedData.
1188
+ // hideRowEditMode runs before softRefreshData and may restore cells
1189
+ // before preparedData is updated, leaving them with stale values.
1190
+ self.gridElement.find('tr.' + self.options.cssClassNames.tableRow + ':not(.always-new-row)').each(function () {
1191
+ var $row = $(this),
1192
+ rowId = +$row.attr('data-row-id'),
1193
+ rowData = self.preparedData[rowId];
1194
+ if (!rowData) return;
1195
+
1196
+ $row.find('td.' + self.options.cssClassNames.tableCell + ':not(.cell-editing)').each(function () {
1197
+ var $cell = $(this),
1198
+ colId = +$cell.attr('data-col-id'),
1199
+ colDef = self.preparedHeaderData[colId],
1200
+ value;
1201
+
1202
+ if (colDef && !colDef.customExpression && colDef.type !== 'custom') {
1203
+ value = _.get(rowData, colDef.field);
1204
+ var newText;
1205
+ if (self.Utils.isDefined(value) && value !== null) {
1206
+ newText = '' + value;
1207
+ } else if (self.options.filterNullRecords && value === null) {
1208
+ newText = 'null';
1209
+ } else {
1210
+ newText = '';
1211
+ }
1212
+ if ($cell.text() !== newText) {
1213
+ $cell.text(newText);
1214
+ $cell.attr('title', newText);
1215
+ }
1216
+ }
1217
+ });
1218
+ });
1219
+
1220
+ // Re-register conditional class watchers with fresh row clones
1221
+ _.forEach(preparedData, function (row, index) {
1222
+ var _row = _.clone(row),
1223
+ rowIndex = isScrollOrOnDemand ? startRowIndex + index - 1 : index;
1224
+ _row.$index = rowIndex + 1;
1225
+
1226
+ self.options.registerRowNgClassWatcher(_row, rowIndex);
1227
+
1228
+ self.preparedHeaderData.forEach(function (colDef, colId) {
1229
+ self.options.registerColNgClassWatcher(_row, colDef, rowIndex, colId);
1230
+ });
1231
+ });
1232
+
1233
+ // Reset action state flags (mirrors _getGridTemplate cleanup)
1234
+ if (!this.options.isdynamictable) {
1235
+ this.options.setLastActionPerformed(this.options.ACTIONS.DEFAULT);
1236
+ this.options.setIsDataUpdatedByUser(false);
1237
+ this.options.clearActionRowIndex();
1238
+ }
1239
+
1240
+ // Refresh row action buttons and expansion buttons with updated row data.
1241
+ // Scope to data rows only — exclude always-new-row which has no backing data entry.
1242
+ var $dataRows = self.gridElement.find('tr.' + self.options.cssClassNames.tableRow + ':not(.always-new-row)');
1243
+ this.options.clearRowActions();
1244
+ this._appendRowExpansionButtons($dataRows);
1245
+ this._appendRowActions($dataRows);
1246
+ // Regenerated action / expansion DOM — re-bind handlers (attachEventHandlers only runs on full render).
1247
+ this._bindRowActionsAndExpansionClickHandlers($dataRows);
1248
+
1249
+ // Re-render summary row if present
1250
+ if (this.options.summaryRow) {
1251
+ var $summaryRowHtm = $(this._getSummaryRowTemplate());
1252
+ this.tableContainer.find('tfoot').remove();
1253
+ if (this.options.data.length) {
1254
+ this.tableContainer.append($summaryRowHtm);
1255
+ }
1256
+ this.gridFooter = this.tableContainer.find('tfoot');
1257
+ }
1258
+
1259
+ // Invalidate row detail (expansion) cache so collapse+re-expand
1260
+ // creates fresh views with updated data.
1261
+ if (_.isFunction(this.options.invalidateRowDetailCache)) {
1262
+ this.options.invalidateRowDetailCache(preparedData);
1263
+ }
1264
+
1265
+ // Refresh this.items with fresh data references from the updated dataset
1266
+ // so that selecteditem / currentItem reflect the new values.
1267
+ if (_.isFunction(this.options.refreshSelectedItems)) {
1268
+ this.options.refreshSelectedItems();
1269
+ }
1270
+
1271
+ // Fire datarender event callback
1272
+ if (_.isFunction(this.options.onDataRender)) {
1273
+ this.options.onDataRender();
1274
+ }
1275
+ },
1134
1276
  //Populate row data with default data
1135
1277
  setDefaultRowData: function (rowData) {
1136
1278
  _.forEach(this.preparedHeaderData, function (colDef) {
@@ -1551,8 +1693,24 @@ $.widget('wm.datatable', {
1551
1693
 
1552
1694
  /* Sets the options for the grid. */
1553
1695
  _setOption: function (key, value) {
1696
+ const isSortEnabled = v => v !== false && v !== 'false';
1697
+ let prev;
1698
+
1699
+ if (key === 'enableSort') {
1700
+ prev = isSortEnabled(this.options.enableSort);
1701
+ }
1554
1702
  this._super(key, value);
1555
1703
  switch (key) {
1704
+ case 'enableSort':
1705
+ if (this.tableContainer?.length) {
1706
+ this.refreshSortHeader(); // Refresh the table header and update column widths when sort is enabled/disabled
1707
+ }
1708
+ break;
1709
+ case 'enableColumnSelection':
1710
+ if (this.tableContainer?.length) {
1711
+ this.refreshSortHeader(); // Refresh the table header and update column widths when column selection is enabled/disabled
1712
+ }
1713
+ break;
1556
1714
  case 'showHeader':
1557
1715
  this._toggleHeader();
1558
1716
  this._toggleSearch();
@@ -1635,6 +1793,17 @@ $.widget('wm.datatable', {
1635
1793
  return this.options;
1636
1794
  },
1637
1795
 
1796
+ /** function to rebuild table header and update column widths. */
1797
+ refreshSortHeader: function () {
1798
+ this._prepareHeaderData();
1799
+ if (this.options.showHeader) {
1800
+ this._renderHeader();
1801
+ this._attachHeaderEventHandlers();
1802
+ }
1803
+ this.setColGroupWidths();
1804
+ this.addOrRemoveScroll();
1805
+ },
1806
+
1638
1807
  /* Toggles the table header visibility. */
1639
1808
  _toggleHeader: function () {
1640
1809
  if (this.gridHeaderElement) {
@@ -1647,6 +1816,7 @@ $.widget('wm.datatable', {
1647
1816
  this.setDefaultColsData();
1648
1817
  if (this.options.showHeader) {
1649
1818
  this._renderHeader();
1819
+ this._attachHeaderEventHandlers(); // Attach all event handlers specifically for the table header
1650
1820
  }
1651
1821
  },
1652
1822
 
@@ -1729,11 +1899,14 @@ $.widget('wm.datatable', {
1729
1899
  if (this.options.showRadioColumn) {
1730
1900
  $radio = $row.find('td input[rowSelectInput]:radio:not(:disabled)');
1731
1901
  $radio.prop('checked', selected);
1902
+ $radio.attr('checked', selected ? 'checked' : null); //
1732
1903
  this.preparedData[rowId]._checked = selected;
1733
1904
  }
1734
1905
  if (this.options.multiselect) {
1735
1906
  $checkbox = $row.find('td input[name="gridMultiSelect"]:checkbox:not(:disabled)');
1736
1907
  $checkbox.prop('checked', selected);
1908
+ $checkbox.attr('checked', selected ? 'checked' : null);
1909
+ $checkbox.closest('label').toggleClass('unchecked', !selected);
1737
1910
  $checkbox.siblings('span.sr-only').text(selected ? 'Row Selected' : 'Row Deselected');
1738
1911
  this.preparedData[rowId]._checked = selected;
1739
1912
  // if we check header checkbox(select/unselect all the records) then updating selectAll checkbox state is not required.
@@ -1767,6 +1940,7 @@ $.widget('wm.datatable', {
1767
1940
  $headerCheckbox.prop('checked', false);
1768
1941
  }
1769
1942
  },
1943
+
1770
1944
  // triggered on capture phase of click listener.
1771
1945
  // sets the selected rowdata on click.
1772
1946
  rowClickHandlerOnCapture: function (e, $row, options) {
@@ -2155,6 +2329,10 @@ $.widget('wm.datatable', {
2155
2329
  var rowId = parseInt($row.attr('data-row-id'), 10);
2156
2330
  return rowId >= this.preparedData.length || $row.hasClass('always-new-row');
2157
2331
  },
2332
+ _getWmVariableRowLocator: function (rowId) {
2333
+ var liveRow = this.options.data[parseInt(rowId, 10)];
2334
+ return (liveRow && liveRow.$$wmSourceListIndex != null) ? liveRow.$$wmSourceListIndex : (liveRow || parseInt(rowId, 10));
2335
+ },
2158
2336
  /* Toggles the edit state of a row. */
2159
2337
  toggleEditRow: function (e, options) {
2160
2338
  if(e?.target && $(e.target).closest('.app-menu').length) {
@@ -2360,8 +2538,9 @@ $.widget('wm.datatable', {
2360
2538
  return;
2361
2539
  }
2362
2540
  }
2363
- if(this.options?.getDataSource()?.category== "wm.Variable") {
2364
- editOptions.rowindex = rowindex;
2541
+ var ds = this.options.getDataSource && this.options.getDataSource();
2542
+ if (ds && ds.category === 'wm.Variable') {
2543
+ editOptions.rowindex = self._getWmVariableRowLocator(rowindex);
2365
2544
  }
2366
2545
  this.options.afterRowUpdate(rowData, e, onSaveSuccess, editOptions);
2367
2546
  }
@@ -2514,6 +2693,10 @@ $.widget('wm.datatable', {
2514
2693
  return;
2515
2694
  }
2516
2695
  }
2696
+ var deleteDs = this.options.getDataSource && this.options.getDataSource();
2697
+ if (deleteDs && deleteDs.category === 'wm.Variable') {
2698
+ options.rowindex = this._getWmVariableRowLocator(rowId);
2699
+ }
2517
2700
  if (_.isFunction(this.options.onRowDelete)) {
2518
2701
  className = this.options.cssClassNames.deleteRow;
2519
2702
  isActiveRow = $row.attr('class').indexOf('active') !== -1;
@@ -2626,8 +2809,10 @@ $.widget('wm.datatable', {
2626
2809
  /* Handles table sorting. */
2627
2810
  sortHandler: function (e) {
2628
2811
  e.stopImmediatePropagation();
2629
- // If header span is clicked and column selection is enabled, call header click
2812
+ // If header span is clicked and column selection is enabled, call header click hand
2813
+ //Calling the column selection handler so that column selection works even when the bind exp changes later from false to true.
2630
2814
  if ($(e.target).hasClass('header-data') && this.options.enableColumnSelection) {
2815
+ this.columnSelectionHandler(e);
2631
2816
  this.headerClickHandler(e);
2632
2817
  }
2633
2818
  var $e = $(e.target),
@@ -2892,10 +3077,31 @@ $.widget('wm.datatable', {
2892
3077
  this.sortHandler(event);
2893
3078
  }
2894
3079
  },
3080
+ /**
3081
+ * Binds jQuery handlers on save/cancel row-action buttons and row-expansion controls.
3082
+ * Must run after DOM for those controls is replaced (e.g. softRefreshData).
3083
+ */
3084
+ _bindRowActionsAndExpansionClickHandlers: function ($scope) {
3085
+ var self = this;
3086
+ if (this.options.rowActions.length) {
3087
+ $scope.find('.cancel-edit-row-button').off('click');
3088
+ $scope.find('.save-edit-row-button').off('click');
3089
+ $scope.find('.cancel-edit-row-button').on('click', {action: 'cancel'}, this.toggleEditRow.bind(this));
3090
+ $scope.find('.save-edit-row-button').on('click', {action: 'save'}, this.toggleEditRow.bind(this));
3091
+ }
3092
+ $scope.find('[data-identifier="rowExpansionButtons"]').off('click');
3093
+ $scope.find('[data-identifier="rowExpansionButtons"]').on('click', function (e) {
3094
+ var $row = $(e.target).closest('tr.app-datagrid-row');
3095
+ if ($(this).find('.app-button').attr('disabled')) {
3096
+ return;
3097
+ }
3098
+ self.toggleExpandRow(+$row.attr('data-row-id'), undefined, e);
3099
+ });
3100
+ },
3101
+
2895
3102
  /* Attaches all event handlers for the table. */
2896
3103
  attachEventHandlers: function ($htm) {
2897
- var $header = this.gridHeaderElement,
2898
- self = this;
3104
+ var self = this;
2899
3105
 
2900
3106
  if (this.options.enableRowSelection) {
2901
3107
  $htm[0].removeEventListener('click', this.rowClickHandlerOnCapture.bind(this));
@@ -2917,40 +3123,15 @@ $.widget('wm.datatable', {
2917
3123
  $htm.on('keydown', this.onKeyDown.bind(this));
2918
3124
  }
2919
3125
 
2920
- if ($header) {
2921
- if (this.options.enableColumnSelection) {
2922
- $header.find('th[data-col-selectable]').off('click', this.columnSelectionHandler.bind(this));
2923
- $header.find('th[data-col-selectable]').on('click', this.columnSelectionHandler.bind(this));
2924
- } else {
2925
- $header.find('th[data-col-selectable]').off('click');
2926
- }
3126
+ this._attachHeaderEventHandlers();
2927
3127
 
2928
- if (this.options.enableSort) {
2929
- if (this.options.enableColumnSelection) {
2930
- $header.find('th[data-col-sortable] .header-data').off('click', this.sortHandler.bind(this));
2931
- $header.find('th[data-col-sortable] .header-data').on('click', this.sortHandler.bind(this));
2932
- $header.find('th[data-col-sortable]').off('keydown', this.keydownHandler.bind(this));
2933
- $header.find('th[data-col-sortable]').on('keydown', this.keydownHandler.bind(this));
2934
- } else {
2935
- $header.find('th[data-col-sortable]').off('click', this.sortHandler.bind(this));
2936
- $header.find('th[data-col-sortable]').on('click', this.sortHandler.bind(this));
2937
- $header.find('th[data-col-sortable]').off('keydown', this.keydownHandler.bind(this));
2938
- $header.find('th[data-col-sortable]').on('keydown', this.keydownHandler.bind(this));
2939
- }
2940
- } else {
2941
- if (this.options.enableColumnSelection) {
2942
- $header.find('th[data-col-sortable] .header-data').off('click');
2943
- } else {
2944
- $header.find('th[data-col-sortable]').off('click');
2945
- }
2946
- }
2947
- }
2948
3128
  if (this.options.rowActions.length) {
2949
3129
  $htm.find('.cancel-edit-row-button').off('click');
2950
3130
  $htm.find('.save-edit-row-button').off('click');
2951
3131
  $htm.find('.cancel-edit-row-button').on('click', {action: 'cancel'}, this.toggleEditRow.bind(this));
2952
3132
  $htm.find('.save-edit-row-button').on('click', {action: 'save'}, this.toggleEditRow.bind(this));
2953
3133
  }
3134
+ this._bindRowActionsAndExpansionClickHandlers($htm);
2954
3135
  if (self.options.editmode === self.CONSTANTS.QUICK_EDIT) {
2955
3136
  $htm.on('focus', 'tr.app-datagrid-row', function (e) {
2956
3137
  var $row = $(e.currentTarget);
@@ -2971,13 +3152,23 @@ $.widget('wm.datatable', {
2971
3152
  isRelatedTargetRowAction = $relatedTarget.closest('span.actions-column').length>0,
2972
3153
  isTargetGridAction = $relatedTarget.closest('div.app-datagrid-actions').length>0,
2973
3154
  isRelatedTargetGridAction = $relatedTarget.closest('div.app-datagrid-actions').length>0,
3155
+ currentGridId = $($target.closest('.app-grid'))?.attr('widget-id'),
3156
+ relatedGrid = $relatedTarget.closest('.app-grid')[0],
3157
+ relatedGridId = relatedGrid ? $(relatedGrid).attr('widget-id') : null,
3158
+ isRelatedGridDifferent = relatedGrid && relatedGridId !== currentGridId,
2974
3159
  invalidTargets = '.row-editing:not(".always-new-row"), .row-action-button, .app-datagrid-cell, .caption, button.btn-time, button.btn-date';
2975
3160
 
2976
3161
  //Check if the focus out element is outside the grid or some special elements
2977
3162
  function isInvalidTarget() {
2978
- if (!$relatedTarget.closest('.app-grid').length) {
3163
+ // If relatedTarget is not in any grid, it's invalid (outside all tables)
3164
+ if (!relatedGrid) {
2979
3165
  return true;
2980
3166
  }
3167
+ // If relatedTarget is in a different grid, allow save (return false)
3168
+ if (isRelatedGridDifferent) {
3169
+ return false; // NOT invalid - we should save the grid
3170
+ }
3171
+ // If relatedTarget is in SAME grid, check if it's an invalid target within this grid
2981
3172
  return $relatedTarget.is(invalidTargets);
2982
3173
  }
2983
3174
 
@@ -2994,8 +3185,9 @@ $.widget('wm.datatable', {
2994
3185
  }
2995
3186
  // Save the Row if any button from Grid action is clicked / AddRow action is
2996
3187
  // triggered from the Row Actions
2997
- if (!isTargetGridAction && !isTargetRowAction) {
2998
- //Save the row on last column of the data table. Do not save the row if focus is out of input file.
3188
+ if (!isTargetGridAction && !isTargetRowAction && !isRelatedGridDifferent) {
3189
+ // If focus moved to a DIFFERENT grid, allow save (skip isLastColumn check)
3190
+ // Otherwise Save the row on last column of the data table. Do not save the row if focus is out of input file.
2999
3191
  if (!isLastColumn || (isLastColumn && (e.relatedTarget === null || $(e.target).is('tr.app-datagrid-row'))) || $target.hasClass("file-upload")) {
3000
3192
  return;
3001
3193
  }
@@ -3005,7 +3197,9 @@ $.widget('wm.datatable', {
3005
3197
  'action': 'save',
3006
3198
  'noMsg': true,
3007
3199
  'success': function (skipFocus, error, isNewRow) {
3008
- if (!isNewRow) {
3200
+ // Only navigate to next row if staying in SAME grid
3201
+ // If switching to DIFFERENT grid, let that grid's click handler take over
3202
+ if (!isNewRow && !isRelatedGridDifferent) {
3009
3203
  self.editSuccessHandler(skipFocus, error, e, $row,false);
3010
3204
  }
3011
3205
  }
@@ -3024,6 +3218,42 @@ $.widget('wm.datatable', {
3024
3218
  self.toggleExpandRow(+$row.attr('data-row-id'), undefined, e);
3025
3219
  });
3026
3220
  },
3221
+ /**
3222
+ * Function which attaches all event handlers specifically for the table header.
3223
+ * Needed when table header is rebuilt (e.g. showHeader toggle) without a full _renderGrid
3224
+ */
3225
+ _attachHeaderEventHandlers: function () {
3226
+ var $header = this.gridHeaderElement;
3227
+ if (!$header || !$header.length) {
3228
+ return;
3229
+ }
3230
+ if (this.options.enableColumnSelection) {
3231
+ $header.find('th[data-col-selectable]').off('click', this.columnSelectionHandler.bind(this));
3232
+ $header.find('th[data-col-selectable]').on('click', this.columnSelectionHandler.bind(this));
3233
+ } else {
3234
+ $header.find('th[data-col-selectable]').off('click');
3235
+ }
3236
+
3237
+ if (!(this.options.enableSort === false || this.options.enableSort === 'false')) {
3238
+ if (this.options.enableColumnSelection) {
3239
+ $header.find('th[data-col-sortable] .header-data').off('click', this.sortHandler.bind(this));
3240
+ $header.find('th[data-col-sortable] .header-data').on('click', this.sortHandler.bind(this));
3241
+ $header.find('th[data-col-sortable]').off('keydown', this.keydownHandler.bind(this));
3242
+ $header.find('th[data-col-sortable]').on('keydown', this.keydownHandler.bind(this));
3243
+ } else {
3244
+ $header.find('th[data-col-sortable]').off('click', this.sortHandler.bind(this));
3245
+ $header.find('th[data-col-sortable]').on('click', this.sortHandler.bind(this));
3246
+ $header.find('th[data-col-sortable]').off('keydown', this.keydownHandler.bind(this));
3247
+ $header.find('th[data-col-sortable]').on('keydown', this.keydownHandler.bind(this));
3248
+ }
3249
+ } else {
3250
+ if (this.options.enableColumnSelection) {
3251
+ $header.find('th[data-col-sortable] .header-data').off('click');
3252
+ } else {
3253
+ $header.find('th[data-col-sortable]').off('click');
3254
+ }
3255
+ }
3256
+ },
3027
3257
  expandRow: function(rowId) {
3028
3258
  this.toggleExpandRow(rowId, true)
3029
3259
  },
@@ -3209,11 +3439,19 @@ $.widget('wm.datatable', {
3209
3439
  self.gridHeaderElement.parent().prop('scrollLeft', this.scrollLeft);
3210
3440
  });
3211
3441
  if (!this.options.showHeader) {
3442
+ this.tableContainer.find('colgroup').remove(); // Remove the colgroup from the table container when showHeader is false
3212
3443
  this.tableContainer.append($colgroup);
3213
3444
  this.gridHeaderElement.hide();
3214
3445
  //this.gridElement.prepend($colgroup.clone());
3215
3446
  return;
3216
3447
  } else {
3448
+ /* _toggleHeader() removes thead from the DOM when toggling showHeader; gridHeaderElement
3449
+ * still references the detached thead. Re-insert it before the tbody so the header
3450
+ * is visible when bind changes from false to true. */
3451
+ if (this.gridHeaderElement.length && this.tableContainer?.length &&
3452
+ !this.tableContainer[0].contains(this.gridHeaderElement[0])) {
3453
+ this.tableContainer.prepend(this.gridHeaderElement); // Re-insert the table header before the tbody when showHeader is true
3454
+ }
3217
3455
  this.gridHeaderElement.show();
3218
3456
  }
3219
3457
  $header = headerTemplate.header;
@@ -3262,7 +3500,7 @@ $.widget('wm.datatable', {
3262
3500
  $headerCol.hide();
3263
3501
  }
3264
3502
  });
3265
-
3503
+ this.tableContainer.find('colgroup').remove(); // Remove the colgroup from the table container when showHeader is false
3266
3504
  /*For mobile view, append header to the main table only*/
3267
3505
  if (this.options.isMobile) {
3268
3506
  this.tableContainer.append($colgroup);
@@ -3546,8 +3784,8 @@ $.widget('wm.datatable', {
3546
3784
  this.dataStatusContainer.hide();
3547
3785
  } else {
3548
3786
  // [WMS-23839] always show load more btn if show view less btn is true
3549
- if (this.options.isNavTypeScrollOrOndemand() &&
3550
- (state === 'nodata' ||
3787
+ if (this.options.isNavTypeScrollOrOndemand() &&
3788
+ (state === 'nodata' ||
3551
3789
  (this.options.getCurrentPage && typeof this.options.getCurrentPage === 'function' &&
3552
3790
  this.options.getPageCount && typeof this.options.getPageCount === 'function' &&
3553
3791
  (this.options.getCurrentPage() == this.options.getPageCount()) && !this.options.showviewlessbutton))) {
@@ -3560,7 +3798,7 @@ $.widget('wm.datatable', {
3560
3798
  if (state === 'nodata') {
3561
3799
  this.dataStatusContainer.css('height', 'auto');
3562
3800
  this.dataStatus.contentHeight = 0;
3563
- } else if (this.options.isNavTypeScrollOrOndemand() &&
3801
+ } else if (this.options.isNavTypeScrollOrOndemand() &&
3564
3802
  this.options.getCurrentPage && typeof this.options.getCurrentPage === 'function' &&
3565
3803
  this.options.getCurrentPage() > 1){
3566
3804
  // showing the loading icon only for the first page
@@ -3572,7 +3810,7 @@ $.widget('wm.datatable', {
3572
3810
  this.dataStatusContainer.css('height', this.dataStatus.height > this.dataStatus.contentHeight ? 'auto' : this.dataStatus.contentHeight);
3573
3811
  }
3574
3812
  }
3575
- if (!isScrollOrOndemand || (isScrollOrOndemand &&
3813
+ if (!isScrollOrOndemand || (isScrollOrOndemand &&
3576
3814
  this.options.getCurrentPage && typeof this.options.getCurrentPage === 'function' &&
3577
3815
  this.options.getCurrentPage() === 1)) {
3578
3816
  this.gridContainer.addClass("show-msg");
@@ -3698,7 +3936,7 @@ $.widget('wm.datatable', {
3698
3936
  // this.gridElement[0].removeEventListener('click', this.boundRowClickHandlerOnCapture, true);
3699
3937
  // this.boundRowClickHandlerOnCapture = null;
3700
3938
  // }
3701
-
3939
+
3702
3940
  // MEMORY LEAK FIX: Remove all jQuery event handlers
3703
3941
  if (this.gridHeaderElement) {
3704
3942
  this.gridHeaderElement.off();
@@ -3715,12 +3953,12 @@ $.widget('wm.datatable', {
3715
3953
  if (this.gridContainer) {
3716
3954
  this.gridContainer.off();
3717
3955
  }
3718
-
3956
+
3719
3957
  // MEMORY LEAK FIX: Clear all internal structures
3720
3958
  this.preparedData = [];
3721
3959
  this.preparedHeaderData = [];
3722
3960
  this.compiledCellTemplates = {};
3723
-
3961
+
3724
3962
  this.element.text('');
3725
3963
  window.clearTimeout(this.refreshGridTimeout);
3726
3964
  }
@@ -405,6 +405,9 @@
405
405
  if (parts[0] === 'Variables' || parts[0] === 'Widgets') {
406
406
  return `${parts[0]}.${parts[1]}`;
407
407
  }
408
+ if (parts[0] === 'App' && parts[1] === 'activePage' && parts[2] === 'Variables' && parts[3]) {
409
+ return `App.activePage.Variables.${parts[3]}`;
410
+ }
408
411
  };
409
412
  const widgetChildAttrs = (() => {
410
413
  const childAttrs = new Map();