@zeedhi/teknisa-components-common 1.97.3 → 1.97.6

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.
@@ -1455,6 +1455,22 @@ class GridController {
1455
1455
  }
1456
1456
  }
1457
1457
 
1458
+ /**
1459
+ * Extracts properties from an object based on a list of properties
1460
+ * @param obj object whose properties will be extracted
1461
+ * @param props array of strings with the properties to be extracted
1462
+ * @returns object containing the extracted properties
1463
+ */
1464
+ const extractProperties = (obj, props) => {
1465
+ const result = {};
1466
+ props.forEach((prop) => {
1467
+ if (obj[prop] !== undefined) {
1468
+ result[prop] = obj[prop];
1469
+ }
1470
+ });
1471
+ return result;
1472
+ };
1473
+
1458
1474
  const DynamicFilterOperations = {
1459
1475
  CONTAINS: true,
1460
1476
  NOT_CONTAINS: true,
@@ -2101,6 +2117,19 @@ class TekGridColumn extends GridColumnEditable {
2101
2117
  }
2102
2118
  }
2103
2119
 
2120
+ /**
2121
+ * Thrown when a row has incomplete group information.
2122
+ * Provides details about the missing groups and the problematic row.
2123
+ */
2124
+ class IncompleteGroupsError extends Error {
2125
+ constructor(row, missingGroups) {
2126
+ const rowPreview = JSON.stringify(row);
2127
+ const message = `Row groups are incomplete. Missing groups: ${missingGroups.join(', ')}.\nRow data:\n${rowPreview}`;
2128
+ super(message);
2129
+ this.name = 'IncompleteGroupsError';
2130
+ }
2131
+ }
2132
+
2104
2133
  /* TekGrid Class */
2105
2134
  class TekGrid extends GridEditable {
2106
2135
  /**
@@ -2524,9 +2553,10 @@ class TekGrid extends GridEditable {
2524
2553
  this.groups.forEach((group) => {
2525
2554
  group.lastGroupHeaderRow.children.push(row);
2526
2555
  });
2527
- this.groupedData.push(Object.assign(Object.assign({}, row), { groupHeaders: this.groups
2528
- .map((group) => group.lastGroupHeaderRow)
2529
- .filter(Boolean) }));
2556
+ const groupHeaders = this.groups
2557
+ .map((group) => group.lastGroupHeaderRow)
2558
+ .filter(Boolean);
2559
+ this.groupedData.push(Object.assign(Object.assign({}, row), { groupHeaders }));
2530
2560
  this.calcSummary(row);
2531
2561
  });
2532
2562
  if (this.groupedData.length > 0) {
@@ -2582,14 +2612,14 @@ class TekGrid extends GridEditable {
2582
2612
  return;
2583
2613
  }
2584
2614
  for (let i = this.groups.length - 1; i >= groupIndex; i -= 1) {
2585
- if (this.groups[i].initialized) {
2586
- const groupFooterRow = Object.assign({ groupFooter: true, groupIndex: i, groupHeaders: [], groupLabel: this.groups[i].lastGroupHeaderRow.groupLabel, groupValue: this.groups[i].lastGroupHeaderRow.groupValue }, this.summaryData(this.groups[i].footer));
2587
- // add header for outer groups
2588
- for (let g = 0; g < i; g += 1) {
2589
- groupFooterRow.groupHeaders.push(this.groups[g].lastGroupHeaderRow);
2590
- }
2591
- this.groupedData.push(groupFooterRow);
2615
+ if (!this.groups[i].initialized)
2616
+ return;
2617
+ const groupFooterRow = Object.assign({ groupFooter: true, groupIndex: i, groupHeaders: [], groupLabel: this.groups[i].lastGroupHeaderRow.groupLabel, groupValue: this.groups[i].lastGroupHeaderRow.groupValue }, this.summaryData(this.groups[i].footer));
2618
+ // add header for outer groups
2619
+ for (let g = 0; g < i; g += 1) {
2620
+ groupFooterRow.groupHeaders.push(this.groups[g].lastGroupHeaderRow);
2592
2621
  }
2622
+ this.groupedData.push(groupFooterRow);
2593
2623
  }
2594
2624
  }
2595
2625
  resetFooterVariables(group) {
@@ -2826,6 +2856,7 @@ class TekGrid extends GridEditable {
2826
2856
  */
2827
2857
  groupRowClick(row, event, element) {
2828
2858
  if (!this.preventRowClick) {
2859
+ this.datasource.currentRow = row;
2829
2860
  this.callEvent('groupRowClick', {
2830
2861
  event,
2831
2862
  element,
@@ -2869,6 +2900,157 @@ class TekGrid extends GridEditable {
2869
2900
  getFilterInputs(columnName) {
2870
2901
  return this.gridBase.getFilterInputs(columnName);
2871
2902
  }
2903
+ /**
2904
+ * Adds new row to the datasource data and pushes it to the editedRows
2905
+ * @param row Row
2906
+ * @param position whether the new Row will be inserted at the beginning or end of the data array
2907
+ */
2908
+ addNewRow(row, position = 'end') {
2909
+ const _super = Object.create(null, {
2910
+ addNewRow: { get: () => super.addNewRow }
2911
+ });
2912
+ return __awaiter(this, void 0, void 0, function* () {
2913
+ if (!this.groupColumns.length) {
2914
+ yield _super.addNewRow.call(this, row, position);
2915
+ return;
2916
+ }
2917
+ this.insertRowInDatasource(row, position);
2918
+ this.saveRowReference(row);
2919
+ this.addGroupedRow(row, position);
2920
+ this.editing = true;
2921
+ });
2922
+ }
2923
+ /**
2924
+ * Takes a row and adds it to the selected group (datasource.currentRow)
2925
+ * @param row the new row to be added
2926
+ * @param position the position, at the beginning of the group or at the end
2927
+ */
2928
+ addToSelection(row, position = 'end') {
2929
+ const { currentRow } = this.datasource;
2930
+ // if the currentRow is a group, use the groupRow as reference
2931
+ const referenceRow = this.isGroupHeader(currentRow) ? currentRow.groupRow : currentRow;
2932
+ // extract the group properties from the referenceRow to add to the new row
2933
+ const groupProperties = extractProperties(referenceRow, this.groupColumnNames);
2934
+ const rowWithGroups = Object.assign(Object.assign({}, row), groupProperties);
2935
+ return this.addNewRow(rowWithGroups, position);
2936
+ }
2937
+ /**
2938
+ * Adds a new row to the groupedData array
2939
+ * @param row the new row to be added
2940
+ * @param position the position, at the beginning of the group or at the end
2941
+ */
2942
+ addGroupedRow(row, position) {
2943
+ const missingGroups = this.findMissingGroups(row);
2944
+ if (missingGroups.length > 0) {
2945
+ throw new IncompleteGroupsError(row, missingGroups);
2946
+ }
2947
+ const { index, group } = this.findLastGroupingIndex(row);
2948
+ const groupHeaders = [...group.groupHeaders, group];
2949
+ const newGroupedRow = Object.assign(Object.assign({}, row), { groupHeaders });
2950
+ if (position === 'end') {
2951
+ const childrenLength = group.children.length;
2952
+ this.groupedData.splice(index + childrenLength + 1, 0, newGroupedRow);
2953
+ }
2954
+ else {
2955
+ this.groupedData.splice(index + 1, 0, newGroupedRow);
2956
+ }
2957
+ groupHeaders.forEach((groupHeader) => {
2958
+ // open all groups, making the new row visible to the user
2959
+ groupHeader.groupOpened = true;
2960
+ this.addRowToGroupChildren(row, groupHeader, position);
2961
+ });
2962
+ }
2963
+ findMissingGroups(row) {
2964
+ return this.groupColumnNames.filter((group) => row[group] === undefined);
2965
+ }
2966
+ /**
2967
+ * Finds the last group (most internal group) where a row should be inserted
2968
+ * @param row to be inserted
2969
+ * @returns the index and the group where the row should be inserted
2970
+ */
2971
+ findLastGroupingIndex(row) {
2972
+ const index = this.groupedData.findIndex((groupRow) => {
2973
+ // ignore rows that are not headers or not the last grouping (most internal groping)
2974
+ if (!this.isGroupHeader(groupRow) || !this.isLastGrouping(groupRow))
2975
+ return false;
2976
+ // if the new added row is not part of the group, ignore it
2977
+ if (groupRow.groupValue !== row[groupRow.groupColumnName])
2978
+ return false;
2979
+ if (this.groupColumns.length === 1)
2980
+ return true;
2981
+ const matchHeaders = groupRow.groupHeaders.every((groupHeader) => (groupHeader.groupValue === row[groupHeader.groupColumnName]));
2982
+ return matchHeaders;
2983
+ });
2984
+ if (index === -1) {
2985
+ // No existing group found, so we create the full group hierarchy
2986
+ return this.createGroupHierarchyForRow(row);
2987
+ }
2988
+ const group = this.groupedData[index];
2989
+ return { index, group };
2990
+ }
2991
+ /**
2992
+ * Creates a group hierarchy for a new row, creating intermediate groups if needed
2993
+ * @param row to be inserted
2994
+ * @returns the index and the group where the row should be inserted
2995
+ */
2996
+ createGroupHierarchyForRow(row) {
2997
+ let groupHeaders = [];
2998
+ let insertionIndex = this.groupedData.length; // default to end
2999
+ this.groups.forEach((column) => {
3000
+ const groupValue = row[column.name];
3001
+ const existingGroupIndex = this.groupedData.findIndex((groupRow) => this.isGroupHeader(groupRow)
3002
+ && groupRow.groupColumnName === column.name
3003
+ && groupRow.groupValue === groupValue
3004
+ && groupRow.groupHeaders.every((h, j) => h.groupValue === groupHeaders[j].groupValue));
3005
+ if (existingGroupIndex !== -1) {
3006
+ const existingGroup = this.groupedData[existingGroupIndex];
3007
+ groupHeaders = [...existingGroup.groupHeaders, existingGroup];
3008
+ insertionIndex = existingGroupIndex;
3009
+ return;
3010
+ }
3011
+ // Create new group header
3012
+ const newGroup = {
3013
+ group: true,
3014
+ groupHeader: true,
3015
+ groupValue,
3016
+ groupColumnName: column.name,
3017
+ groupHeaders: [...groupHeaders],
3018
+ children: [],
3019
+ groupRow: row,
3020
+ groupIndex: groupHeaders.length,
3021
+ groupLabel: I18n.translate(column.label),
3022
+ groupOpened: true,
3023
+ };
3024
+ // Insert right after last insertionIndex
3025
+ insertionIndex += 1;
3026
+ this.groupedData.splice(insertionIndex, 0, newGroup);
3027
+ groupHeaders.push(newGroup);
3028
+ });
3029
+ const group = groupHeaders[groupHeaders.length - 1]; // the most inner group
3030
+ return { index: insertionIndex, group };
3031
+ }
3032
+ /**
3033
+ * Adds a row to the children of a group
3034
+ */
3035
+ addRowToGroupChildren(row, group, position) {
3036
+ if (position === 'end') {
3037
+ group.children.push(row);
3038
+ return;
3039
+ }
3040
+ group.children.unshift(row);
3041
+ }
3042
+ /**
3043
+ * Checks if a row is a group header, adding typescript type-checking
3044
+ */
3045
+ isGroupHeader(row) {
3046
+ return row.groupHeader;
3047
+ }
3048
+ /**
3049
+ * Checks if a row is the last grouping of the grid (the most internal grouping)
3050
+ */
3051
+ isLastGrouping(group) {
3052
+ return group.groupColumnName === this.groupColumns[this.groupColumns.length - 1].name;
3053
+ }
2872
3054
  }
2873
3055
 
2874
3056
  class TekGridColumnsButtonController extends IterableColumnsButtonController {
@@ -1459,6 +1459,22 @@
1459
1459
  }
1460
1460
  }
1461
1461
 
1462
+ /**
1463
+ * Extracts properties from an object based on a list of properties
1464
+ * @param obj object whose properties will be extracted
1465
+ * @param props array of strings with the properties to be extracted
1466
+ * @returns object containing the extracted properties
1467
+ */
1468
+ const extractProperties = (obj, props) => {
1469
+ const result = {};
1470
+ props.forEach((prop) => {
1471
+ if (obj[prop] !== undefined) {
1472
+ result[prop] = obj[prop];
1473
+ }
1474
+ });
1475
+ return result;
1476
+ };
1477
+
1462
1478
  const DynamicFilterOperations = {
1463
1479
  CONTAINS: true,
1464
1480
  NOT_CONTAINS: true,
@@ -2105,6 +2121,19 @@
2105
2121
  }
2106
2122
  }
2107
2123
 
2124
+ /**
2125
+ * Thrown when a row has incomplete group information.
2126
+ * Provides details about the missing groups and the problematic row.
2127
+ */
2128
+ class IncompleteGroupsError extends Error {
2129
+ constructor(row, missingGroups) {
2130
+ const rowPreview = JSON.stringify(row);
2131
+ const message = `Row groups are incomplete. Missing groups: ${missingGroups.join(', ')}.\nRow data:\n${rowPreview}`;
2132
+ super(message);
2133
+ this.name = 'IncompleteGroupsError';
2134
+ }
2135
+ }
2136
+
2108
2137
  /* TekGrid Class */
2109
2138
  class TekGrid extends common.GridEditable {
2110
2139
  /**
@@ -2528,9 +2557,10 @@
2528
2557
  this.groups.forEach((group) => {
2529
2558
  group.lastGroupHeaderRow.children.push(row);
2530
2559
  });
2531
- this.groupedData.push(Object.assign(Object.assign({}, row), { groupHeaders: this.groups
2532
- .map((group) => group.lastGroupHeaderRow)
2533
- .filter(Boolean) }));
2560
+ const groupHeaders = this.groups
2561
+ .map((group) => group.lastGroupHeaderRow)
2562
+ .filter(Boolean);
2563
+ this.groupedData.push(Object.assign(Object.assign({}, row), { groupHeaders }));
2534
2564
  this.calcSummary(row);
2535
2565
  });
2536
2566
  if (this.groupedData.length > 0) {
@@ -2586,14 +2616,14 @@
2586
2616
  return;
2587
2617
  }
2588
2618
  for (let i = this.groups.length - 1; i >= groupIndex; i -= 1) {
2589
- if (this.groups[i].initialized) {
2590
- const groupFooterRow = Object.assign({ groupFooter: true, groupIndex: i, groupHeaders: [], groupLabel: this.groups[i].lastGroupHeaderRow.groupLabel, groupValue: this.groups[i].lastGroupHeaderRow.groupValue }, this.summaryData(this.groups[i].footer));
2591
- // add header for outer groups
2592
- for (let g = 0; g < i; g += 1) {
2593
- groupFooterRow.groupHeaders.push(this.groups[g].lastGroupHeaderRow);
2594
- }
2595
- this.groupedData.push(groupFooterRow);
2619
+ if (!this.groups[i].initialized)
2620
+ return;
2621
+ const groupFooterRow = Object.assign({ groupFooter: true, groupIndex: i, groupHeaders: [], groupLabel: this.groups[i].lastGroupHeaderRow.groupLabel, groupValue: this.groups[i].lastGroupHeaderRow.groupValue }, this.summaryData(this.groups[i].footer));
2622
+ // add header for outer groups
2623
+ for (let g = 0; g < i; g += 1) {
2624
+ groupFooterRow.groupHeaders.push(this.groups[g].lastGroupHeaderRow);
2596
2625
  }
2626
+ this.groupedData.push(groupFooterRow);
2597
2627
  }
2598
2628
  }
2599
2629
  resetFooterVariables(group) {
@@ -2830,6 +2860,7 @@
2830
2860
  */
2831
2861
  groupRowClick(row, event, element) {
2832
2862
  if (!this.preventRowClick) {
2863
+ this.datasource.currentRow = row;
2833
2864
  this.callEvent('groupRowClick', {
2834
2865
  event,
2835
2866
  element,
@@ -2873,6 +2904,157 @@
2873
2904
  getFilterInputs(columnName) {
2874
2905
  return this.gridBase.getFilterInputs(columnName);
2875
2906
  }
2907
+ /**
2908
+ * Adds new row to the datasource data and pushes it to the editedRows
2909
+ * @param row Row
2910
+ * @param position whether the new Row will be inserted at the beginning or end of the data array
2911
+ */
2912
+ addNewRow(row, position = 'end') {
2913
+ const _super = Object.create(null, {
2914
+ addNewRow: { get: () => super.addNewRow }
2915
+ });
2916
+ return __awaiter(this, void 0, void 0, function* () {
2917
+ if (!this.groupColumns.length) {
2918
+ yield _super.addNewRow.call(this, row, position);
2919
+ return;
2920
+ }
2921
+ this.insertRowInDatasource(row, position);
2922
+ this.saveRowReference(row);
2923
+ this.addGroupedRow(row, position);
2924
+ this.editing = true;
2925
+ });
2926
+ }
2927
+ /**
2928
+ * Takes a row and adds it to the selected group (datasource.currentRow)
2929
+ * @param row the new row to be added
2930
+ * @param position the position, at the beginning of the group or at the end
2931
+ */
2932
+ addToSelection(row, position = 'end') {
2933
+ const { currentRow } = this.datasource;
2934
+ // if the currentRow is a group, use the groupRow as reference
2935
+ const referenceRow = this.isGroupHeader(currentRow) ? currentRow.groupRow : currentRow;
2936
+ // extract the group properties from the referenceRow to add to the new row
2937
+ const groupProperties = extractProperties(referenceRow, this.groupColumnNames);
2938
+ const rowWithGroups = Object.assign(Object.assign({}, row), groupProperties);
2939
+ return this.addNewRow(rowWithGroups, position);
2940
+ }
2941
+ /**
2942
+ * Adds a new row to the groupedData array
2943
+ * @param row the new row to be added
2944
+ * @param position the position, at the beginning of the group or at the end
2945
+ */
2946
+ addGroupedRow(row, position) {
2947
+ const missingGroups = this.findMissingGroups(row);
2948
+ if (missingGroups.length > 0) {
2949
+ throw new IncompleteGroupsError(row, missingGroups);
2950
+ }
2951
+ const { index, group } = this.findLastGroupingIndex(row);
2952
+ const groupHeaders = [...group.groupHeaders, group];
2953
+ const newGroupedRow = Object.assign(Object.assign({}, row), { groupHeaders });
2954
+ if (position === 'end') {
2955
+ const childrenLength = group.children.length;
2956
+ this.groupedData.splice(index + childrenLength + 1, 0, newGroupedRow);
2957
+ }
2958
+ else {
2959
+ this.groupedData.splice(index + 1, 0, newGroupedRow);
2960
+ }
2961
+ groupHeaders.forEach((groupHeader) => {
2962
+ // open all groups, making the new row visible to the user
2963
+ groupHeader.groupOpened = true;
2964
+ this.addRowToGroupChildren(row, groupHeader, position);
2965
+ });
2966
+ }
2967
+ findMissingGroups(row) {
2968
+ return this.groupColumnNames.filter((group) => row[group] === undefined);
2969
+ }
2970
+ /**
2971
+ * Finds the last group (most internal group) where a row should be inserted
2972
+ * @param row to be inserted
2973
+ * @returns the index and the group where the row should be inserted
2974
+ */
2975
+ findLastGroupingIndex(row) {
2976
+ const index = this.groupedData.findIndex((groupRow) => {
2977
+ // ignore rows that are not headers or not the last grouping (most internal groping)
2978
+ if (!this.isGroupHeader(groupRow) || !this.isLastGrouping(groupRow))
2979
+ return false;
2980
+ // if the new added row is not part of the group, ignore it
2981
+ if (groupRow.groupValue !== row[groupRow.groupColumnName])
2982
+ return false;
2983
+ if (this.groupColumns.length === 1)
2984
+ return true;
2985
+ const matchHeaders = groupRow.groupHeaders.every((groupHeader) => (groupHeader.groupValue === row[groupHeader.groupColumnName]));
2986
+ return matchHeaders;
2987
+ });
2988
+ if (index === -1) {
2989
+ // No existing group found, so we create the full group hierarchy
2990
+ return this.createGroupHierarchyForRow(row);
2991
+ }
2992
+ const group = this.groupedData[index];
2993
+ return { index, group };
2994
+ }
2995
+ /**
2996
+ * Creates a group hierarchy for a new row, creating intermediate groups if needed
2997
+ * @param row to be inserted
2998
+ * @returns the index and the group where the row should be inserted
2999
+ */
3000
+ createGroupHierarchyForRow(row) {
3001
+ let groupHeaders = [];
3002
+ let insertionIndex = this.groupedData.length; // default to end
3003
+ this.groups.forEach((column) => {
3004
+ const groupValue = row[column.name];
3005
+ const existingGroupIndex = this.groupedData.findIndex((groupRow) => this.isGroupHeader(groupRow)
3006
+ && groupRow.groupColumnName === column.name
3007
+ && groupRow.groupValue === groupValue
3008
+ && groupRow.groupHeaders.every((h, j) => h.groupValue === groupHeaders[j].groupValue));
3009
+ if (existingGroupIndex !== -1) {
3010
+ const existingGroup = this.groupedData[existingGroupIndex];
3011
+ groupHeaders = [...existingGroup.groupHeaders, existingGroup];
3012
+ insertionIndex = existingGroupIndex;
3013
+ return;
3014
+ }
3015
+ // Create new group header
3016
+ const newGroup = {
3017
+ group: true,
3018
+ groupHeader: true,
3019
+ groupValue,
3020
+ groupColumnName: column.name,
3021
+ groupHeaders: [...groupHeaders],
3022
+ children: [],
3023
+ groupRow: row,
3024
+ groupIndex: groupHeaders.length,
3025
+ groupLabel: core.I18n.translate(column.label),
3026
+ groupOpened: true,
3027
+ };
3028
+ // Insert right after last insertionIndex
3029
+ insertionIndex += 1;
3030
+ this.groupedData.splice(insertionIndex, 0, newGroup);
3031
+ groupHeaders.push(newGroup);
3032
+ });
3033
+ const group = groupHeaders[groupHeaders.length - 1]; // the most inner group
3034
+ return { index: insertionIndex, group };
3035
+ }
3036
+ /**
3037
+ * Adds a row to the children of a group
3038
+ */
3039
+ addRowToGroupChildren(row, group, position) {
3040
+ if (position === 'end') {
3041
+ group.children.push(row);
3042
+ return;
3043
+ }
3044
+ group.children.unshift(row);
3045
+ }
3046
+ /**
3047
+ * Checks if a row is a group header, adding typescript type-checking
3048
+ */
3049
+ isGroupHeader(row) {
3050
+ return row.groupHeader;
3051
+ }
3052
+ /**
3053
+ * Checks if a row is the last grouping of the grid (the most internal grouping)
3054
+ */
3055
+ isLastGrouping(group) {
3056
+ return group.groupColumnName === this.groupColumns[this.groupColumns.length - 1].name;
3057
+ }
2876
3058
  }
2877
3059
 
2878
3060
  class TekGridColumnsButtonController extends common.IterableColumnsButtonController {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeedhi/teknisa-components-common",
3
- "version": "1.97.3",
3
+ "version": "1.97.6",
4
4
  "description": "Teknisa Components Common",
5
5
  "author": "Zeedhi <zeedhi@teknisa.com>",
6
6
  "license": "ISC",
@@ -32,5 +32,5 @@
32
32
  "peerDependencies": {
33
33
  "@zeedhi/core": "~1.97.0"
34
34
  },
35
- "gitHead": "2aea0238fb3917e862b21fd096f2851f74e44337"
35
+ "gitHead": "8f016098621c6cc389bb9a761fe52d8f169d07e2"
36
36
  }