@wavemaker/app-ng-runtime 11.14.1-3.6306 → 11.14.1-32.647748

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 (67) hide show
  1. package/components/base/bundles/index.umd.js +6 -22
  2. package/components/base/esm2022/pipes/custom-pipes.mjs +1 -1
  3. package/components/base/esm2022/widgets/common/base/base.component.mjs +8 -24
  4. package/components/base/fesm2022/index.mjs +7 -23
  5. package/components/base/fesm2022/index.mjs.map +1 -1
  6. package/components/chart/bundles/index.umd.js +2 -2
  7. package/components/chart/esm2022/chart.utils.mjs +3 -3
  8. package/components/chart/fesm2022/index.mjs +2 -2
  9. package/components/chart/fesm2022/index.mjs.map +1 -1
  10. package/components/containers/wizard/bundles/index.umd.js +21 -2
  11. package/components/containers/wizard/esm2022/wizard-step/wizard-step.component.mjs +3 -1
  12. package/components/containers/wizard/esm2022/wizard.component.mjs +20 -3
  13. package/components/containers/wizard/fesm2022/index.mjs +21 -2
  14. package/components/containers/wizard/fesm2022/index.mjs.map +1 -1
  15. package/components/containers/wizard/wizard-step/wizard-step.component.d.ts +2 -0
  16. package/components/data/pagination/bundles/index.umd.js +6 -1
  17. package/components/data/pagination/esm2022/pagination.component.mjs +7 -2
  18. package/components/data/pagination/fesm2022/index.mjs +6 -1
  19. package/components/data/pagination/fesm2022/index.mjs.map +1 -1
  20. package/components/data/table/bundles/index.umd.js +331 -23
  21. package/components/data/table/esm2022/table-action/table-action.directive.mjs +8 -1
  22. package/components/data/table/esm2022/table-column/table-column.directive.mjs +108 -4
  23. package/components/data/table/esm2022/table-column-group/table-column-group.directive.mjs +9 -1
  24. package/components/data/table/esm2022/table-cud.directive.mjs +22 -5
  25. package/components/data/table/esm2022/table-filter.directive.mjs +43 -4
  26. package/components/data/table/esm2022/table-row/table-row.directive.mjs +8 -1
  27. package/components/data/table/esm2022/table-row-action/table-row-action.directive.mjs +8 -1
  28. package/components/data/table/esm2022/table.component.mjs +134 -15
  29. package/components/data/table/fesm2022/index.mjs +333 -25
  30. package/components/data/table/fesm2022/index.mjs.map +1 -1
  31. package/components/data/table/table-action/table-action.directive.d.ts +3 -2
  32. package/components/data/table/table-column/table-column.directive.d.ts +3 -2
  33. package/components/data/table/table-column-group/table-column-group.directive.d.ts +3 -2
  34. package/components/data/table/table-cud.directive.d.ts +3 -1
  35. package/components/data/table/table-filter.directive.d.ts +3 -1
  36. package/components/data/table/table-row/table-row.directive.d.ts +3 -2
  37. package/components/data/table/table-row-action/table-row-action.directive.d.ts +3 -2
  38. package/components/data/table/table.component.d.ts +10 -0
  39. package/components/page/default/bundles/index.umd.js +1 -0
  40. package/components/page/default/esm2022/page.directive.mjs +2 -1
  41. package/components/page/default/fesm2022/index.mjs +1 -0
  42. package/components/page/default/fesm2022/index.mjs.map +1 -1
  43. package/core/bundles/index.umd.js +185 -330
  44. package/core/esm2022/public_api.mjs +2 -2
  45. package/core/esm2022/utils/watcher.mjs +189 -328
  46. package/core/fesm2022/index.mjs +192 -330
  47. package/core/fesm2022/index.mjs.map +1 -1
  48. package/core/public_api.d.ts +1 -1
  49. package/core/utils/watcher.d.ts +5 -26
  50. package/locales/moment-timezone/moment-timezone-with-data.js +179 -27
  51. package/npm-shrinkwrap.json +2 -2
  52. package/package-lock.json +2 -2
  53. package/package.json +1 -1
  54. package/runtime/base/bundles/index.umd.js +20 -46
  55. package/runtime/base/components/base-prefab.component.d.ts +6 -4
  56. package/runtime/base/esm2022/components/app-component/app.component.mjs +2 -7
  57. package/runtime/base/esm2022/components/base-page.component.mjs +2 -8
  58. package/runtime/base/esm2022/components/base-partial.component.mjs +3 -11
  59. package/runtime/base/esm2022/components/base-prefab.component.mjs +21 -21
  60. package/runtime/base/esm2022/components/base-spa-page.component.mjs +2 -9
  61. package/runtime/base/fesm2022/index.mjs +21 -47
  62. package/runtime/base/fesm2022/index.mjs.map +1 -1
  63. package/runtime/dynamic/bundles/index.umd.js +4 -0
  64. package/runtime/dynamic/esm2022/app/app.config.mjs +6 -2
  65. package/runtime/dynamic/fesm2022/index.mjs +5 -1
  66. package/runtime/dynamic/fesm2022/index.mjs.map +1 -1
  67. package/scripts/datatable/datatable.js +336 -33
@@ -108,7 +108,7 @@
108
108
  const ROW_OPS_FIELD = 'rowOperations';
109
109
  const noop = () => { };
110
110
  const isInputBodyWrapper = target => {
111
- const classes = ['.dropdown-menu', '.uib-typeahead-match', '.modal-dialog', '.toast'];
111
+ const classes = ['.dropdown-menu', '.uib-typeahead-match', '.modal-dialog', '.toast', '.bs-datepicker-container'];
112
112
  let isInput = false;
113
113
  classes.forEach(cls => {
114
114
  if (target.closest(cls).length) {
@@ -129,6 +129,19 @@
129
129
  };
130
130
  class TableComponent extends i4.StylableComponent {
131
131
  static { this.initializeProps = registerProps$5(); }
132
+ /** wm.Variable + inline or quickedit (editmode is string from markup; normalize case). */
133
+ isModelVariableInlineOrQuickEdit() {
134
+ if (lodashEs.get(this.datasource, 'category') !== 'wm.Variable') {
135
+ return false;
136
+ }
137
+ const em = String(this.editmode || '').toLowerCase();
138
+ return em === 'quickedit' || em === 'inline';
139
+ }
140
+ /** True while an inline/quickedit row save is updating the grid (incl. delayed navigator emit). */
141
+ shouldSoftRefreshAfterModelVarRowSave() {
142
+ return this.isModelVariableInlineOrQuickEdit()
143
+ && (this._isInlineEditSave || this._modelVarEditSoftRefreshActiveUntil > Date.now());
144
+ }
132
145
  set gridData(newValue) {
133
146
  this.isDataLoading = false;
134
147
  // Fix for [WMS-24012]: hide no datafound msg when variableInflight is true and dataset is empty
@@ -196,7 +209,14 @@
196
209
  console.warn('Error in beforedatarender event callback, continuing with table rendering:', error);
197
210
  }
198
211
  }
199
- this.setDataGridOption('data', i2.getClonedObject(this._gridData));
212
+ if (this.shouldSoftRefreshAfterModelVarRowSave()) {
213
+ const clonedData = i2.getClonedObject(this._gridData);
214
+ this.gridOptions.data = clonedData;
215
+ this.callDataGridMethod('softRefreshData', clonedData);
216
+ }
217
+ else {
218
+ this.setDataGridOption('data', i2.getClonedObject(this._gridData));
219
+ }
200
220
  }
201
221
  }
202
222
  get gridData() {
@@ -249,6 +269,8 @@
249
269
  this.formfields = {};
250
270
  this.enablesort = true;
251
271
  this.showviewlessbutton = false;
272
+ /** Covers async navigator/dataset updates after inline/quickedit save (wm.Variable). */
273
+ this._modelVarEditSoftRefreshActiveUntil = 0;
252
274
  this.primaryKey = [];
253
275
  this.selectedItems = [];
254
276
  this.selectedItemChange = new rxjs.Subject();
@@ -587,7 +609,25 @@
587
609
  this.prevData = i2.getClonedObject(row);
588
610
  },
589
611
  afterRowUpdate: (row, e, callBack, options) => {
590
- this.updateRecord(lodashEs.extend({}, options, { row, 'prevData': options.rowindex ? options.rowindex : this.prevData, 'event': e, 'callBack': callBack }));
612
+ const isModelVarInlineOrQuick = this.isModelVariableInlineOrQuickEdit();
613
+ if (isModelVarInlineOrQuick) {
614
+ this._isInlineEditSave = true;
615
+ // Inline save triggers navigatePage / dataset emit asynchronously; a
616
+ // microtask timeout cleared the flag before gridData ran → full rerender.
617
+ this._modelVarEditSoftRefreshActiveUntil = Date.now() + 550;
618
+ }
619
+ i2.muteWatchers();
620
+ const v = lodashEs.get(this.datasource, 'category') === 'wm.Variable' && options && options.rowindex != null;
621
+ this.updateRecord(lodashEs.extend({}, options, { row, prevData: v ? options.rowindex : this.prevData, event: e, callBack }));
622
+ setTimeout(() => {
623
+ i2.unMuteWatchers();
624
+ if (isModelVarInlineOrQuick) {
625
+ setTimeout(() => {
626
+ this._isInlineEditSave = false;
627
+ this._modelVarEditSoftRefreshActiveUntil = 0;
628
+ }, 520);
629
+ }
630
+ }, 10);
591
631
  },
592
632
  onBeforeRowUpdate: (row, e, options) => {
593
633
  return this.invokeEventCallback('beforerowupdate', { $event: e, $data: row, row, options: options });
@@ -659,11 +699,67 @@
659
699
  this.customExprCompiledTl = {};
660
700
  this.customExprCompiledSummaryTl = {};
661
701
  },
702
+ updateCustomExpressionContexts: (preparedData) => {
703
+ const viewCount = this.customExprViewRef.length;
704
+ for (let i = 0; i < viewCount; i++) {
705
+ const viewRef = this.customExprViewRef.get(i);
706
+ if (!viewRef || !viewRef.context || !viewRef.context.row || viewRef.context.row.$index === undefined) {
707
+ continue;
708
+ }
709
+ const existingRow = viewRef.context.row;
710
+ const rowIndex = existingRow.$index - 1;
711
+ const dataRow = preparedData[rowIndex];
712
+ if (!dataRow) {
713
+ continue;
714
+ }
715
+ Object.keys(dataRow).forEach(key => {
716
+ if (key !== '$$index' && key !== '$$pk') {
717
+ existingRow[key] = dataRow[key];
718
+ }
719
+ });
720
+ existingRow.$isFirst = existingRow.$index === 1;
721
+ existingRow.$isLast = this.gridData.length === existingRow.$index;
722
+ viewRef.markForCheck();
723
+ }
724
+ },
662
725
  clearRowDetailExpression: () => {
663
726
  this.rowDetailViewRef.clear();
664
727
  this.rowDefMap = {};
665
728
  this.rowDefInstances = {};
666
729
  },
730
+ invalidateRowDetailCache: (preparedData) => {
731
+ this.rowDefMap = {};
732
+ this.rowDefInstances = {};
733
+ // Update existing row detail view contexts in-place so currently
734
+ // expanded partials pick up the new row data via change detection.
735
+ const viewCount = this.rowDetailViewRef.length;
736
+ for (let i = 0; i < viewCount; i++) {
737
+ const viewRef = this.rowDetailViewRef.get(i);
738
+ if (!viewRef || !viewRef.context || !viewRef.context.row) {
739
+ continue;
740
+ }
741
+ const existingRow = viewRef.context.row;
742
+ const rowIndex = existingRow.$index - 1;
743
+ const dataRow = preparedData[rowIndex];
744
+ if (!dataRow) {
745
+ continue;
746
+ }
747
+ Object.keys(dataRow).forEach(key => {
748
+ if (key !== '$$index' && key !== '$$pk') {
749
+ existingRow[key] = dataRow[key];
750
+ }
751
+ });
752
+ viewRef.markForCheck();
753
+ }
754
+ },
755
+ refreshSelectedItems: () => {
756
+ const selectedRows = this.callDataGridMethod('getSelectedRows');
757
+ if (selectedRows.length) {
758
+ this.items.length = 0;
759
+ selectedRows.forEach(row => this.items.push(row));
760
+ this.selectedItemChange.next(selectedRows);
761
+ }
762
+ },
667
763
  generateCustomExpressions: (rowData, index, summaryRow) => {
668
764
  const row = this.getClonedRowObject(rowData);
669
765
  const compileTemplate = (tmpl) => {
@@ -1125,7 +1221,9 @@
1125
1221
  this.applyProps.forEach(args => this.callDataGridMethod(...args));
1126
1222
  if (this.editmode === i4.EDIT_MODE.QUICK_EDIT) {
1127
1223
  this.documentClickBind = this._documentClickBind.bind(this);
1128
- document.addEventListener('click', this.documentClickBind);
1224
+ // Use capture phase so a click on another grid (which calls stopPropagation) still reaches this
1225
+ // listener and lets the current grid save/close its editing row.
1226
+ document.addEventListener('click', this.documentClickBind, true);
1129
1227
  }
1130
1228
  }
1131
1229
  getConfiguredState() {
@@ -1351,8 +1449,10 @@
1351
1449
  }
1352
1450
  }
1353
1451
  ngOnDestroy() {
1452
+ this._modelVarEditSoftRefreshActiveUntil = 0;
1453
+ this._isInlineEditSave = false;
1354
1454
  // MEMORY LEAK FIX: Remove document click listener
1355
- document.removeEventListener('click', this.documentClickBind);
1455
+ document.removeEventListener('click', this.documentClickBind, true);
1356
1456
  // MEMORY LEAK FIX: Unsubscribe from navigator watches
1357
1457
  if (this.navigatorResultWatch) {
1358
1458
  this.navigatorResultWatch.unsubscribe();
@@ -1411,7 +1511,6 @@
1411
1511
  this.selectedItems = [];
1412
1512
  this.fieldDefs = [];
1413
1513
  this.fullFieldDefs = [];
1414
- this.gridData = [];
1415
1514
  this.__fullData = null;
1416
1515
  // MEMORY LEAK FIX: Destroy jQuery datatable widget before clearing gridOptions
1417
1516
  if (this.datagridElement && this.datagridElement.datatable) {
@@ -1524,6 +1623,7 @@
1524
1623
  row.$isLast = this.gridData.length === row.$index;
1525
1624
  delete row.$$index;
1526
1625
  delete row.$$pk;
1626
+ delete row.$$wmSourceListIndex;
1527
1627
  return row;
1528
1628
  }
1529
1629
  handleLoading(data) {
@@ -1765,6 +1865,14 @@
1765
1865
  // Apply filter and sort, if data is refreshed through Refresh data method
1766
1866
  if (!this.isNavigationEnabled() && this._isClientSearch) {
1767
1867
  data = i2.getClonedObject(serviceData);
1868
+ if (lodashEs.isObject(data) && !lodashEs.isArray(data)) {
1869
+ data = [data];
1870
+ }
1871
+ if (lodashEs.get(this.datasource, 'category') === 'wm.Variable' && lodashEs.isArray(data)) {
1872
+ for (let i = 0; i < data.length; i++) {
1873
+ data[i].$$wmSourceListIndex = i;
1874
+ }
1875
+ }
1768
1876
  data = this.getSearchResult(data, this.filterInfo);
1769
1877
  data = this.getSortResult(data, this.sortInfo);
1770
1878
  this.serverData = data;
@@ -1946,10 +2054,7 @@
1946
2054
  this.headerselectall = false;
1947
2055
  this.setDataGridOption("headerselectall", false);
1948
2056
  }
1949
- // Process dataset when datasource is set, especially if dataset was previously skipped
1950
- if (this.datasource && this.dataset !== undefined) {
1951
- this.watchVariableDataSet(this.dataset);
1952
- }
2057
+ this.watchVariableDataSet(this.dataset);
1953
2058
  this.onDataSourceChange();
1954
2059
  break;
1955
2060
  case 'dataset':
@@ -2094,6 +2199,10 @@
2094
2199
  case 'show':
2095
2200
  if (nv) {
2096
2201
  this.invokeEventCallback('show');
2202
+ // recompute layout now that the table is visible again
2203
+ if (this.datagridElement) {
2204
+ this.redraw(false);
2205
+ }
2097
2206
  }
2098
2207
  else {
2099
2208
  this.invokeEventCallback('hide');
@@ -2268,6 +2377,11 @@
2268
2377
  if (lodashEs.isObject(data) && !lodashEs.isArray(data)) {
2269
2378
  data = [data];
2270
2379
  }
2380
+ if (lodashEs.get(this.datasource, 'category') === 'wm.Variable' && lodashEs.isArray(data)) {
2381
+ for (let i = 0; i < data.length; i++) {
2382
+ data[i].$$wmSourceListIndex = i;
2383
+ }
2384
+ }
2271
2385
  data = this.getSearchResult(data, this.filterInfo);
2272
2386
  data = this.getSortResult(data, this.sortInfo);
2273
2387
  return data;
@@ -2328,11 +2442,16 @@
2328
2442
  }
2329
2443
  _documentClickBind(event) {
2330
2444
  const $target = event.target;
2331
- // If click triggered from same grid or a dialog, do not save the row
2445
+ // If the click is within this table, a dialog, or a date picker, do not save the row.
2332
2446
  if (this.$element[0].contains($target) || event.target.doctype || isInputBodyWrapper($($target))) {
2333
2447
  return;
2334
2448
  }
2335
- this.callDataGridMethod('saveRow');
2449
+ // If click is inside another datatable, that table's edit handler saves this row — skip to avoid a duplicate save. [WMS-31104]
2450
+ if ($($target).closest('.app-datagrid').length) {
2451
+ return;
2452
+ }
2453
+ // Click is outside all tables — save the editing row without returning focus to this table. [WMS-31104]
2454
+ this.callDataGridMethod('saveRow', undefined, true);
2336
2455
  }
2337
2456
  _redraw(forceRender) {
2338
2457
  if (forceRender) {
@@ -2340,7 +2459,7 @@
2340
2459
  }
2341
2460
  else {
2342
2461
  setTimeout(() => {
2343
- this.callDataGridMethod('setColGroupWidths');
2462
+ this.callDataGridMethod(forceRender === false ? 'refreshColumnWidths' : 'setColGroupWidths');
2344
2463
  this.callDataGridMethod('addOrRemoveScroll');
2345
2464
  });
2346
2465
  }
@@ -2670,11 +2789,11 @@
2670
2789
  this.table.toggleMessage(true, 'success', this.table.updatemessage);
2671
2790
  }
2672
2791
  if (this.table.datasource.execute(i2.DataSource.Operation.SUPPORTS_CRUD)) {
2673
- this.table.initiateSelectItem('current', response, undefined, false, options.callBack);
2792
+ this.table.initiateSelectItem('current', response, options.skipFocus, false, options.callBack);
2674
2793
  this.updateVariable(response, options.callBack);
2675
2794
  }
2676
2795
  else if (!this.table.datasource.execute(i2.DataSource.Operation.IS_API_AWARE)) {
2677
- this.table.initiateSelectItem('current', response, undefined, false, options.callBack);
2796
+ this.table.initiateSelectItem('current', response, options.skipFocus, this.table.datasource.category === 'wm.Variable', options.callBack);
2678
2797
  }
2679
2798
  i2.triggerFn(options.success, response);
2680
2799
  this.table.invokeEventCallback('rowupdate', { $event: options.event, $data: response, row: response });
@@ -2695,7 +2814,11 @@
2695
2814
  if (dataSource.execute(i2.DataSource.Operation.SUPPORTS_CRUD) || !dataSource.execute(i2.DataSource.Operation.IS_API_AWARE) || dataSource.category === 'wm.CrudVariable') {
2696
2815
  if (!dataSource.execute(i2.DataSource.Operation.IS_API_AWARE)) {
2697
2816
  const extraOptions = this.generatePath(this.table.binddataset);
2698
- dataSource.execute(i2.DataSource.Operation.SET_ITEM, { prevItem: options.prevData, item: options.row, path: extraOptions.path, parentIndex: extraOptions.index });
2817
+ const item = options.row;
2818
+ if (dataSource.category === 'wm.Variable' && item) {
2819
+ delete item['$$wmSourceListIndex'];
2820
+ }
2821
+ dataSource.execute(i2.DataSource.Operation.SET_ITEM, { prevItem: options.prevData, item, path: extraOptions.path, parentIndex: extraOptions.index });
2699
2822
  this.updateSuccessHandler(options.row, options);
2700
2823
  return;
2701
2824
  }
@@ -2778,7 +2901,14 @@
2778
2901
  if (dataSource.execute(i2.DataSource.Operation.SUPPORTS_CRUD) || !dataSource.execute(i2.DataSource.Operation.IS_API_AWARE) || this.table._isDependent || dataSource.category === 'wm.CrudVariable') {
2779
2902
  if (!dataSource.execute(i2.DataSource.Operation.IS_API_AWARE)) {
2780
2903
  const extraOptions = this.generatePath(this.table.binddataset);
2781
- dataSource.execute(i2.DataSource.Operation.REMOVE_ITEM, { item: options.row, path: extraOptions.path, parentIndex: extraOptions.index });
2904
+ let item = options.row;
2905
+ if (dataSource.category === 'wm.Variable' && options.rowindex != null) {
2906
+ item = options.rowindex;
2907
+ }
2908
+ else if (dataSource.category === 'wm.Variable' && item) {
2909
+ delete item['$$wmSourceListIndex'];
2910
+ }
2911
+ dataSource.execute(i2.DataSource.Operation.REMOVE_ITEM, { item, path: extraOptions.path, parentIndex: extraOptions.index });
2782
2912
  this.deleteSuccessHandler(options.row, undefined, options.evt, options.callBack);
2783
2913
  return;
2784
2914
  }
@@ -2924,6 +3054,12 @@
2924
3054
  this.table.callDataGridMethod('cancelEdit', $row);
2925
3055
  }
2926
3056
  }
3057
+ ngOnDestroy() {
3058
+ // MEMORY LEAK FIX: Clear all references
3059
+ this.table = null;
3060
+ this.dialogService = null;
3061
+ this.app = null;
3062
+ }
2927
3063
  static { this.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0__namespace, type: TableCUDDirective, deps: [{ token: TableComponent, self: true }, { token: i2__namespace.AbstractDialogService }, { token: i2__namespace.App }], target: i0__namespace.ɵɵFactoryTarget.Directive }); }
2928
3064
  static { this.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: TableCUDDirective, isStandalone: true, selector: "[wmTableCUD]", ngImport: i0__namespace }); }
2929
3065
  }
@@ -3235,7 +3371,19 @@
3235
3371
  }
3236
3372
  // Function that checks if a given string is a valid date and returns the timestamp if it is, or NaN if it's not.
3237
3373
  parseDateString(dateString) {
3374
+ if (typeof dateString !== 'string') {
3375
+ return NaN;
3376
+ }
3377
+ const trimmed = dateString.trim();
3378
+ if (!trimmed) {
3379
+ return NaN;
3380
+ }
3238
3381
  const timestamp = Date.parse(dateString);
3382
+ // handling case when string starts with string and end with number
3383
+ const monthRegex = /\b(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:t(?:ember)?)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\b/i;
3384
+ if (/^[A-Za-z]+/.test(trimmed) && /\d+$/.test(trimmed) && !monthRegex.test(trimmed)) {
3385
+ return NaN;
3386
+ }
3239
3387
  if (!isNaN(timestamp)) {
3240
3388
  return timestamp;
3241
3389
  }
@@ -3247,20 +3395,38 @@
3247
3395
  // Returns data sorted using sortObj
3248
3396
  getSortResult(data, sortObj) {
3249
3397
  if (sortObj && sortObj.direction) {
3250
- const fieldValue = lodashEs.get(lodashEs.find(data, sortObj.field), sortObj.field);
3398
+ const sampleItem = lodashEs.find(data, (item) => {
3399
+ const v = lodashEs.get(item, sortObj.field);
3400
+ return typeof v === 'string' && !!v.trim();
3401
+ });
3402
+ const fieldValue = sampleItem ? lodashEs.get(sampleItem, sortObj.field) : undefined;
3251
3403
  const isValidDateString = typeof fieldValue === 'string' ? this.parseDateString(fieldValue) : NaN;
3252
3404
  if (!isNaN(isValidDateString)) { // if the field is a date string
3253
- data = lodashEs.orderBy(data, [(item) => this.parseDateString(item[sortObj.field])], [sortObj.direction]);
3405
+ data = lodashEs.orderBy(data, [
3406
+ (item) => {
3407
+ const v = lodashEs.get(item, sortObj.field);
3408
+ return v === null || v === undefined || v === '' ? 1 : 0;
3409
+ },
3410
+ (item) => {
3411
+ const ts = this.parseDateString(lodashEs.get(item, sortObj.field));
3412
+ return isNaN(ts) ? 1 : 0;
3413
+ },
3414
+ (item) => this.parseDateString(lodashEs.get(item, sortObj.field))
3415
+ ], ['asc', 'asc', sortObj.direction]);
3254
3416
  }
3255
3417
  else if (sortObj.sortBy ? this.table.columns[sortObj.sortBy]?.caseinsensitive : this.table.columns[sortObj.field]?.caseinsensitive) {
3256
3418
  //Fix for [WMS-27505]: Added case-insensitive sorting so that uppercase and lowercase letters are treated the same when sorting.
3257
3419
  data = lodashEs.orderBy(data, [
3420
+ (item) => {
3421
+ const val = lodashEs.get(item, sortObj.field);
3422
+ return val === null || val === undefined || val === '' ? 1 : 0;
3423
+ },
3258
3424
  (item) => {
3259
3425
  const val = lodashEs.get(item, sortObj.field);
3260
3426
  return typeof val === 'string' ? val.toLowerCase() : val;
3261
3427
  },
3262
3428
  (item) => lodashEs.get(item, sortObj.field) // fallback to original value
3263
- ], [sortObj.direction, sortObj.direction]);
3429
+ ], ['asc', sortObj.direction, sortObj.direction]);
3264
3430
  }
3265
3431
  else {
3266
3432
  data = lodashEs.orderBy(data, sortObj.field, sortObj.direction);
@@ -3282,6 +3448,11 @@
3282
3448
  if (lodashEs.isObject(data) && !lodashEs.isArray(data)) {
3283
3449
  data = [data];
3284
3450
  }
3451
+ if (lodashEs.get(this.table.datasource, 'category') === 'wm.Variable' && lodashEs.isArray(data)) {
3452
+ for (let i = 0; i < data.length; i++) {
3453
+ data[i].$$wmSourceListIndex = i;
3454
+ }
3455
+ }
3285
3456
  /*Both the functions return same 'data' if arguments are undefined*/
3286
3457
  data = this.getSearchResult(data, this.table.filterInfo);
3287
3458
  data = this.getSortResult(data, this.table.sortInfo);
@@ -3612,6 +3783,10 @@
3612
3783
  condition: logicalOp
3613
3784
  });
3614
3785
  }
3786
+ ngOnDestroy() {
3787
+ // MEMORY LEAK FIX: Clear table reference
3788
+ this.table = null;
3789
+ }
3615
3790
  static { this.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0__namespace, type: TableFilterSortDirective, deps: [{ token: TableComponent, self: true }], target: i0__namespace.ɵɵFactoryTarget.Directive }); }
3616
3791
  static { this.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: TableFilterSortDirective, isStandalone: true, selector: "[wmTableFilterSort]", ngImport: i0__namespace }); }
3617
3792
  }
@@ -3698,6 +3873,13 @@
3698
3873
  this.buttonDef[key] = nv;
3699
3874
  }
3700
3875
  }
3876
+ ngOnDestroy() {
3877
+ // MEMORY LEAK FIX: Clear table and buttonDef references
3878
+ this.table = null;
3879
+ this.buttonDef = null;
3880
+ // Call parent ngOnDestroy (BaseComponent has cleanup)
3881
+ super.ngOnDestroy();
3882
+ }
3701
3883
  static { this.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0__namespace, type: TableActionDirective, deps: [{ token: i0__namespace.Injector }, { token: TableComponent, optional: true }, { token: 'EXPLICIT_CONTEXT', optional: true }], target: i0__namespace.ɵɵFactoryTarget.Directive }); }
3702
3884
  static { this.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: TableActionDirective, isStandalone: true, selector: "[wmTableAction]", providers: [
3703
3885
  i4.provideAsWidgetRef(TableActionDirective)
@@ -3862,6 +4044,14 @@
3862
4044
  const fieldName = this.group && this.group.name;
3863
4045
  i4.setHeaderConfigForTable(this.table.headerConfig, this.config, fieldName, fieldName ? colIndex : headerIndex);
3864
4046
  }
4047
+ ngOnDestroy() {
4048
+ // MEMORY LEAK FIX: Clear table, group, and config references
4049
+ this.table = null;
4050
+ this.group = null;
4051
+ this.config = null;
4052
+ // Call parent ngOnDestroy (BaseComponent has cleanup)
4053
+ super.ngOnDestroy();
4054
+ }
3865
4055
  static { this.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0__namespace, type: TableColumnGroupDirective, deps: [{ token: i0__namespace.Injector }, { token: TableColumnGroupDirective, optional: true, skipSelf: true }, { token: TableComponent, optional: true }, { token: 'EXPLICIT_CONTEXT', optional: true }], target: i0__namespace.ɵɵFactoryTarget.Directive }); }
3866
4056
  static { this.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: TableColumnGroupDirective, isStandalone: true, selector: "[wmTableColumnGroup]", providers: [
3867
4057
  i4.provideAsWidgetRef(TableColumnGroupDirective)
@@ -4056,6 +4246,10 @@
4056
4246
  }
4057
4247
  // Remove validators for the inline widget and set form to untouched
4058
4248
  removeValidations() {
4249
+ // Guard against component destruction or table/ngform being null
4250
+ if (this.isDestroyed || !this.table || !this.table.ngform) {
4251
+ return;
4252
+ }
4059
4253
  this.table.ngform.markAsUntouched();
4060
4254
  const control = this.getFormControl();
4061
4255
  if (!control) {
@@ -4067,6 +4261,10 @@
4067
4261
  }
4068
4262
  // Apply default|sync|async|prop validators for QuickEdit new row form control
4069
4263
  applyNewRowValidations() {
4264
+ // Guard against component destruction or table/ngform being null
4265
+ if (this.isDestroyed || !this.table || !this.table.ngform) {
4266
+ return;
4267
+ }
4070
4268
  if (!this._checkNewEditableRowControl()) {
4071
4269
  return;
4072
4270
  }
@@ -4083,22 +4281,37 @@
4083
4281
  }
4084
4282
  // Remove validators for the QuickEdit new row widget and set form to untouched
4085
4283
  removeNewRowValidations() {
4284
+ // Guard against component destruction or table/ngform being null
4285
+ if (this.isDestroyed || !this.table || !this.table.ngform) {
4286
+ return;
4287
+ }
4086
4288
  this.table.ngform.markAsUntouched();
4087
4289
  if (!this._checkNewEditableRowControl()) {
4088
4290
  return;
4089
4291
  }
4090
4292
  const control = this.getFormControl('_new');
4293
+ if (!control) {
4294
+ return;
4295
+ }
4091
4296
  control.clearValidators();
4092
4297
  control.clearAsyncValidators();
4093
4298
  control.updateValueAndValidity();
4094
4299
  }
4095
4300
  addFormControl(suffix) {
4301
+ // Guard against component destruction or table/ngform being null
4302
+ if (this.isDestroyed || !this.table || !this.table.ngform) {
4303
+ return;
4304
+ }
4096
4305
  const ctrlName = suffix ? (this.binding + suffix) : this.binding;
4097
4306
  this.table.ngform.addControl(ctrlName, this.table.fb.control(''));
4098
4307
  }
4099
4308
  getFormControl(suffix) {
4309
+ // Guard against component destruction or table/ngform being null
4310
+ if (this.isDestroyed || !this.table || !this.table.ngform) {
4311
+ return null;
4312
+ }
4100
4313
  const ctrlName = suffix ? (this.binding + suffix) : this.binding;
4101
- return this.table.ngform.controls[ctrlName];
4314
+ return this.table.ngform.controls[ctrlName] || null;
4102
4315
  }
4103
4316
  _checkNewEditableRowControl() {
4104
4317
  return this._isNewEditableRow && this.getFormControl('_new');
@@ -4172,12 +4385,16 @@
4172
4385
  }
4173
4386
  // On field value change, apply cascading filter and set validation message
4174
4387
  onValueChange(val, widget) {
4388
+ // Guard against component destruction or table being null
4389
+ if (this.isDestroyed || !this.table) {
4390
+ return;
4391
+ }
4175
4392
  if (val !== null) {
4176
4393
  i4.applyFilterOnField(this.table.datasource, this.widget, this.table.fieldDefs, val, {
4177
4394
  widget: 'edit-widget-type'
4178
4395
  });
4179
4396
  }
4180
- if (this.table.ngform.touched) {
4397
+ if (this.table.ngform && this.table.ngform.touched) {
4181
4398
  this.activeControlType = widget;
4182
4399
  if (widget === 'inlineInstance') {
4183
4400
  this.notifyChanges();
@@ -4435,7 +4652,7 @@
4435
4652
  this.defaultvalue = i4.getDefaultValue(this.defaultvalue, this.type, this.editWidgetType);
4436
4653
  break;
4437
4654
  case 'show':
4438
- this.table.redraw(true);
4655
+ this.table.redraw(false);
4439
4656
  this.showinfilter = this.nativeElement.hasAttribute('showinfilter') ? this.nativeElement.getAttribute('showinfilter') : nv;
4440
4657
  break;
4441
4658
  case 'filterdataset':
@@ -4468,6 +4685,83 @@
4468
4685
  this.table.redraw(true);
4469
4686
  }
4470
4687
  }
4688
+ ngOnDestroy() {
4689
+ // MEMORY LEAK FIX: Clear ContentChildren QueryLists
4690
+ if (this._filterInstances) {
4691
+ try {
4692
+ this._filterInstances.reset([]);
4693
+ }
4694
+ catch (e) {
4695
+ // Silently handle cleanup errors - QueryList might already be destroyed
4696
+ }
4697
+ }
4698
+ if (this._inlineInstances) {
4699
+ try {
4700
+ this._inlineInstances.reset([]);
4701
+ }
4702
+ catch (e) {
4703
+ // Silently handle cleanup errors - QueryList might already be destroyed
4704
+ }
4705
+ }
4706
+ if (this._inlineInstancesNew) {
4707
+ try {
4708
+ this._inlineInstancesNew.reset([]);
4709
+ }
4710
+ catch (e) {
4711
+ // Silently handle cleanup errors - QueryList might already be destroyed
4712
+ }
4713
+ }
4714
+ // MEMORY LEAK FIX: Clear all widget instance references
4715
+ this.filterInstance = null;
4716
+ this.inlineInstance = null;
4717
+ this.inlineInstanceNew = null;
4718
+ // MEMORY LEAK FIX: Clear template references
4719
+ this.customExprTmpl = null;
4720
+ this.inlineWidthTempRef = null;
4721
+ this.filterTemplateRef = null;
4722
+ // MEMORY LEAK FIX: Clear dataset references
4723
+ this._filterDataSet = null;
4724
+ this.dataset = null;
4725
+ this._dataoptions = null;
4726
+ this._datasource = null;
4727
+ // MEMORY LEAK FIX: Clear validation objects
4728
+ if (this.fieldValidations) {
4729
+ try {
4730
+ this.fieldValidations.destroy();
4731
+ }
4732
+ catch (e) {
4733
+ // Silently handle cleanup errors
4734
+ }
4735
+ this.fieldValidations = null;
4736
+ }
4737
+ if (this.fieldValidations_new) {
4738
+ try {
4739
+ this.fieldValidations_new.destroy();
4740
+ }
4741
+ catch (e) {
4742
+ // Silently handle cleanup errors
4743
+ }
4744
+ this.fieldValidations_new = null;
4745
+ }
4746
+ this.syncValidators = [];
4747
+ this.asyncValidators = [];
4748
+ // MEMORY LEAK FIX: Clear filter control reference
4749
+ this.filterControl = null;
4750
+ // MEMORY LEAK FIX: Clear notify fields array
4751
+ if (this.notifyForFields) {
4752
+ this.notifyForFields = [];
4753
+ }
4754
+ // MEMORY LEAK FIX: Clear observe fields array
4755
+ if (this.observeOnFields) {
4756
+ this.observeOnFields = [];
4757
+ }
4758
+ // MEMORY LEAK FIX: Clear table and group references
4759
+ // Note: These are injected dependencies, but clearing helps GC
4760
+ this.table = null;
4761
+ this.group = null;
4762
+ // Call parent ngOnDestroy
4763
+ super.ngOnDestroy();
4764
+ }
4471
4765
  static { this.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0__namespace, type: TableColumnDirective, deps: [{ token: i0__namespace.Injector }, { token: i2__namespace.AppDefaults }, { token: TableComponent, optional: true }, { token: TableColumnGroupDirective, optional: true }, { token: 'filterdataset.bind', attribute: true }, { token: 'dataset.bind', attribute: true }, { token: 'EXPLICIT_CONTEXT', optional: true }], target: i0__namespace.ɵɵFactoryTarget.Directive }); }
4472
4766
  static { this.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: TableColumnDirective, isStandalone: true, selector: "[wmTableColumn]", providers: [
4473
4767
  i4.provideAsWidgetRef(TableColumnDirective)
@@ -4562,6 +4856,13 @@
4562
4856
  this.config.content = this.content;
4563
4857
  }
4564
4858
  }
4859
+ ngOnDestroy() {
4860
+ // MEMORY LEAK FIX: Clear table and config references
4861
+ this.table = null;
4862
+ this.config = null;
4863
+ // Call parent ngOnDestroy (BaseComponent has cleanup)
4864
+ super.ngOnDestroy();
4865
+ }
4565
4866
  static { this.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0__namespace, type: TableRowDirective, deps: [{ token: i0__namespace.Injector }, { token: TableComponent, optional: true, skipSelf: true }, { token: 'EXPLICIT_CONTEXT', optional: true }], target: i0__namespace.ɵɵFactoryTarget.Directive }); }
4566
4867
  static { this.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: TableRowDirective, isStandalone: true, selector: "[wmTableRow]", providers: [
4567
4868
  i4.provideAsWidgetRef(TableRowDirective)
@@ -4643,6 +4944,13 @@
4643
4944
  this.populateAction();
4644
4945
  this.table.registerRowActions(this.buttonDef);
4645
4946
  }
4947
+ ngOnDestroy() {
4948
+ // MEMORY LEAK FIX: Clear table reference and button definition
4949
+ this.table = null;
4950
+ this.buttonDef = null;
4951
+ // Call parent ngOnDestroy (BaseComponent has cleanup)
4952
+ super.ngOnDestroy();
4953
+ }
4646
4954
  static { this.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0__namespace, type: TableRowActionDirective, deps: [{ token: i0__namespace.Injector }, { token: TableComponent, optional: true }, { token: i4.Context, self: true }, { token: 'EXPLICIT_CONTEXT', optional: true }], target: i0__namespace.ɵɵFactoryTarget.Directive }); }
4647
4955
  static { this.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: TableRowActionDirective, isStandalone: true, selector: "[wmTableRowAction]", providers: [
4648
4956
  i4.provideAsWidgetRef(TableRowActionDirective),