@pega/angular-sdk-overrides 23.1.10 → 24.1.10

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 (85) hide show
  1. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.html +7 -4
  2. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.ts +3 -1
  3. package/lib/designSystemExtension/material-vertical-tabs/material-vertical-tabs.component.html +1 -1
  4. package/lib/designSystemExtension/operator/operator.component.ts +10 -5
  5. package/lib/field/auto-complete/auto-complete.component.ts +1 -1
  6. package/lib/field/cancel-alert/cancel-alert.component.ts +0 -2
  7. package/lib/field/check-box/check-box.component.html +16 -15
  8. package/lib/field/check-box/check-box.component.scss +14 -1
  9. package/lib/field/check-box/check-box.component.ts +126 -44
  10. package/lib/field/currency/currency.component.html +15 -6
  11. package/lib/field/currency/currency.component.ts +36 -18
  12. package/lib/field/date-time/date-time.component.html +5 -5
  13. package/lib/field/date-time/date-time.component.ts +15 -37
  14. package/lib/field/decimal/decimal.component.html +14 -4
  15. package/lib/field/decimal/decimal.component.ts +42 -5
  16. package/lib/field/dropdown/dropdown.component.ts +0 -3
  17. package/lib/field/group/group.component.html +1 -1
  18. package/lib/field/group/group.component.ts +4 -0
  19. package/lib/field/multiselect/multiselect.component.html +33 -0
  20. package/lib/field/multiselect/multiselect.component.scss +7 -0
  21. package/lib/field/multiselect/multiselect.component.spec.ts +21 -0
  22. package/lib/field/multiselect/multiselect.component.ts +359 -0
  23. package/lib/field/multiselect/utils.ts +209 -0
  24. package/lib/field/percentage/percentage.component.html +15 -4
  25. package/lib/field/percentage/percentage.component.ts +29 -5
  26. package/lib/field/radio-buttons/radio-buttons.component.ts +0 -3
  27. package/lib/field/rich-text/config-ext.json +10 -0
  28. package/lib/field/rich-text/rich-text.component.html +1 -1
  29. package/lib/field/scalar-list/scalar-list.component.ts +2 -1
  30. package/lib/field/text-area/text-area.component.ts +0 -2
  31. package/lib/field/time/time.component.html +1 -0
  32. package/lib/field/time/time.component.ts +2 -0
  33. package/lib/field/url/url.component.html +1 -0
  34. package/lib/field/url/url.component.ts +2 -0
  35. package/lib/field/user-reference/user-reference.component.html +50 -45
  36. package/lib/field/user-reference/user-reference.component.ts +33 -15
  37. package/lib/infra/Containers/base-components/flow-container-base.component.ts +22 -0
  38. package/lib/infra/Containers/base-components/helper.ts +89 -0
  39. package/lib/infra/Containers/flow-container/flow-container.component.html +8 -3
  40. package/lib/infra/Containers/flow-container/flow-container.component.ts +30 -29
  41. package/lib/infra/Containers/modal-view-container/modal-view-container.component.ts +40 -8
  42. package/lib/infra/Containers/view-container/view-container.component.ts +0 -1
  43. package/lib/infra/assignment/assignment.component.ts +38 -39
  44. package/lib/infra/dashboard-filter/dashboard-filter.component.ts +0 -1
  45. package/lib/infra/defer-load/defer-load.component.ts +5 -8
  46. package/lib/infra/multi-step/multi-step.component.html +1 -1
  47. package/lib/infra/multi-step/multi-step.component.scss +1 -0
  48. package/lib/infra/navbar/navbar.component.html +4 -4
  49. package/lib/infra/navbar/navbar.component.ts +6 -3
  50. package/lib/infra/view/view.component.ts +1 -1
  51. package/lib/template/banner-page/config-ext.json +9 -0
  52. package/lib/template/case-summary/case-summary.component.ts +37 -3
  53. package/lib/template/case-view/case-view.component.html +3 -3
  54. package/lib/template/case-view/case-view.component.scss +2 -0
  55. package/lib/template/case-view/case-view.component.ts +0 -6
  56. package/lib/template/data-reference/data-reference.component.ts +1 -3
  57. package/lib/template/dynamic-tabs/dynamic-tabs.component.ts +0 -1
  58. package/lib/template/field-group-template/field-group-template.component.ts +4 -12
  59. package/lib/template/field-value-list/field-value-list.component.html +7 -2
  60. package/lib/template/field-value-list/field-value-list.component.ts +1 -0
  61. package/lib/template/inline-dashboard-page/config-ext.json +9 -0
  62. package/lib/template/list-view/list-view.component.html +6 -6
  63. package/lib/template/list-view/list-view.component.ts +36 -28
  64. package/lib/template/list-view/listViewHelpers.ts +0 -1
  65. package/lib/template/repeating-structures/repeating-structures.component.ts +1 -2
  66. package/lib/template/simple-table/simple-table.component.ts +0 -2
  67. package/lib/template/simple-table-manual/helpers.ts +1 -1
  68. package/lib/template/simple-table-manual/simple-table-manual.component.html +1 -1
  69. package/lib/template/simple-table-manual/simple-table-manual.component.ts +49 -19
  70. package/lib/template/simple-table-select/simple-table-select.component.ts +2 -4
  71. package/lib/template/wss-nav-bar/wss-nav-bar.component.html +1 -1
  72. package/lib/template/wss-nav-bar/wss-nav-bar.component.ts +2 -1
  73. package/lib/widget/attachment/attachment.component.html +50 -26
  74. package/lib/widget/attachment/attachment.component.scss +118 -0
  75. package/lib/widget/attachment/attachment.component.ts +256 -501
  76. package/lib/widget/case-history/case-history.component.ts +1 -2
  77. package/lib/widget/feed-container/feed-container.component.ts +0 -4
  78. package/lib/widget/file-utility/file-utility.component.html +2 -2
  79. package/lib/widget/file-utility/file-utility.component.ts +13 -17
  80. package/lib/widget/list-utility/list-utility.component.html +1 -1
  81. package/lib/widget/quick-create/config-ext.json +9 -0
  82. package/lib/widget/quick-create/quick-create.component.ts +1 -1
  83. package/lib/widget/todo/todo.component.html +6 -5
  84. package/lib/widget/todo/todo.component.ts +7 -6
  85. package/package.json +1 -1
@@ -0,0 +1,359 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
3
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
4
+ import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
5
+ import { MatChipsModule } from '@angular/material/chips';
6
+ import { MatCheckboxModule } from '@angular/material/checkbox';
7
+ import { MatOptionModule } from '@angular/material/core';
8
+ import { MatFormFieldModule } from '@angular/material/form-field';
9
+ import { MatInputModule } from '@angular/material/input';
10
+ import { MatIconModule } from '@angular/material/icon';
11
+ import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
12
+ import { ComponentMapperComponent } from '@pega/angular-sdk-components';
13
+ import { Utils } from '@pega/angular-sdk-components';
14
+ import { doSearch, getDisplayFieldsMetaData, getGroupDataForItemsTree, preProcessColumns } from './utils';
15
+ import { deleteInstruction, insertInstruction } from '@pega/angular-sdk-components';
16
+
17
+ @Component({
18
+ selector: 'app-multiselect',
19
+ templateUrl: './multiselect.component.html',
20
+ styleUrls: ['./multiselect.component.scss'],
21
+ standalone: true,
22
+ imports: [
23
+ CommonModule,
24
+ ReactiveFormsModule,
25
+ MatFormFieldModule,
26
+ MatInputModule,
27
+ MatAutocompleteModule,
28
+ MatOptionModule,
29
+ MatCheckboxModule,
30
+ MatIconModule,
31
+ MatChipsModule,
32
+ forwardRef(() => ComponentMapperComponent)
33
+ ]
34
+ })
35
+ export class MultiselectComponent implements OnInit, OnDestroy {
36
+ @Input() pConn$: typeof PConnect;
37
+ @Input() formGroup$: FormGroup;
38
+
39
+ // Used with AngularPConnect
40
+ angularPConnectData: AngularPConnectData = {};
41
+
42
+ label$ = '';
43
+ value$ = '';
44
+ bRequired$ = false;
45
+ bDisabled$ = false;
46
+ bVisible$ = true;
47
+ controlName$: string;
48
+ bHasForm$ = true;
49
+ listType: string;
50
+ placeholder: string;
51
+ fieldControl = new FormControl('', null);
52
+ parameters: {};
53
+ hideLabel: boolean;
54
+ configProps$: any;
55
+
56
+ referenceList: any;
57
+ selectionKey: string;
58
+ primaryField: string;
59
+ initialCaseClass: any;
60
+ showSecondaryInSearchOnly = false;
61
+ isGroupData = false;
62
+ referenceType;
63
+ secondaryFields;
64
+ groupDataSource = [];
65
+ matchPosition = 'contains';
66
+ maxResultsDisplay;
67
+ groupColumnsConfig = [{}];
68
+ selectionList;
69
+ listActions: any;
70
+ selectedItems: any[] = [];
71
+ itemsTreeBaseData = [];
72
+ displayFieldMeta: any;
73
+ dataApiObj: any;
74
+ itemsTree: any[] = [];
75
+ trigger: any;
76
+
77
+ constructor(
78
+ private angularPConnect: AngularPConnectService,
79
+ private utils: Utils
80
+ ) {}
81
+
82
+ ngOnInit(): void {
83
+ // First thing in initialization is registering and subscribing to the AngularPConnect service
84
+ this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
85
+ this.controlName$ = this.angularPConnect.getComponentID(this);
86
+
87
+ // Then, continue on with other initialization
88
+ this.checkAndUpdate();
89
+
90
+ if (this.formGroup$) {
91
+ // add control to formGroup
92
+ this.formGroup$.addControl(this.controlName$, this.fieldControl);
93
+ this.fieldControl.setValue(this.value$);
94
+ this.bHasForm$ = true;
95
+ } else {
96
+ this.bHasForm$ = false;
97
+ }
98
+ }
99
+
100
+ ngOnDestroy(): void {
101
+ if (this.formGroup$) {
102
+ this.formGroup$.removeControl(this.controlName$);
103
+ }
104
+
105
+ if (this.angularPConnectData.unsubscribeFn) {
106
+ this.angularPConnectData.unsubscribeFn();
107
+ }
108
+ }
109
+
110
+ // Callback passed when subscribing to store change
111
+ onStateChange() {
112
+ this.checkAndUpdate();
113
+ }
114
+
115
+ checkAndUpdate() {
116
+ // Should always check the bridge to see if the component should
117
+ // update itself (re-render)
118
+ const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this);
119
+
120
+ // ONLY call updateSelf when the component should update
121
+ if (bUpdateSelf) {
122
+ this.updateSelf();
123
+ }
124
+ }
125
+
126
+ // updateSelf
127
+ updateSelf() {
128
+ this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps());
129
+
130
+ let { datasource = [], columns = [{}] } = this.configProps$;
131
+ this.setPropertyValuesFromProps();
132
+
133
+ if (this.referenceList.length > 0) {
134
+ datasource = this.referenceList;
135
+ columns = [
136
+ {
137
+ value: this.primaryField,
138
+ display: 'true',
139
+ useForSearch: true,
140
+ primary: 'true'
141
+ },
142
+ {
143
+ value: this.selectionKey,
144
+ setProperty: this.selectionKey,
145
+ key: 'true'
146
+ }
147
+ ];
148
+ let secondaryColumns: any = [];
149
+ if (this.secondaryFields) {
150
+ secondaryColumns = this.secondaryFields.map(secondaryField => ({
151
+ value: secondaryField,
152
+ display: 'true',
153
+ secondary: 'true',
154
+ useForSearch: 'true'
155
+ }));
156
+ } else {
157
+ secondaryColumns = [
158
+ {
159
+ value: this.selectionKey,
160
+ display: 'true',
161
+ secondary: 'true',
162
+ useForSearch: 'true'
163
+ }
164
+ ];
165
+ }
166
+ if (this.referenceType === 'Case') {
167
+ columns = [...columns, ...secondaryColumns];
168
+ }
169
+ }
170
+
171
+ this.value$ = this.value$ ? this.value$ : '';
172
+ const contextName = this.pConn$.getContextName();
173
+
174
+ const dataConfig = {
175
+ dataSource: datasource,
176
+ groupDataSource: this.groupDataSource,
177
+ isGroupData: this.isGroupData,
178
+ showSecondaryInSearchOnly: this.showSecondaryInSearchOnly,
179
+ parameters: this.parameters,
180
+ matchPosition: this.matchPosition,
181
+ listType: this.listType,
182
+ maxResultsDisplay: this.maxResultsDisplay || '100',
183
+ columns: preProcessColumns(columns),
184
+ groupColumnsConfig: preProcessColumns(this.groupColumnsConfig)
185
+ };
186
+
187
+ const groupsDisplayFieldMeta = this.listType !== 'associated' ? getDisplayFieldsMetaData(dataConfig.groupColumnsConfig) : null;
188
+
189
+ this.itemsTreeBaseData = getGroupDataForItemsTree(this.groupDataSource, groupsDisplayFieldMeta, this.showSecondaryInSearchOnly) || [];
190
+
191
+ this.itemsTree = this.isGroupData ? getGroupDataForItemsTree(this.groupDataSource, groupsDisplayFieldMeta, this.showSecondaryInSearchOnly) : [];
192
+
193
+ this.displayFieldMeta = this.listType !== 'associated' ? getDisplayFieldsMetaData(dataConfig.columns) : null;
194
+
195
+ this.listActions = this.pConn$.getListActions();
196
+ this.pConn$.setReferenceList(this.selectionList);
197
+
198
+ if (this.configProps$.visibility != null) {
199
+ this.bVisible$ = this.utils.getBooleanValue(this.configProps$.visibility);
200
+ }
201
+
202
+ // disabled
203
+ if (this.configProps$.disabled != undefined) {
204
+ this.bDisabled$ = this.utils.getBooleanValue(this.configProps$.disabled);
205
+ }
206
+
207
+ if (this.bDisabled$) {
208
+ this.fieldControl.disable();
209
+ } else {
210
+ this.fieldControl.enable();
211
+ }
212
+
213
+ if (this.listType !== 'associated') {
214
+ PCore.getDataApi()
215
+ ?.init(dataConfig, contextName)
216
+ .then(async dataObj => {
217
+ this.dataApiObj = dataObj;
218
+ if (!this.isGroupData) {
219
+ this.getCaseListBasedOnParams(this.value$ ?? '', '', [...this.selectedItems], [...this.itemsTree]);
220
+ }
221
+ });
222
+ }
223
+ }
224
+
225
+ setPropertyValuesFromProps() {
226
+ this.label$ = this.configProps$.label;
227
+ this.placeholder = this.configProps$.placeholder || '';
228
+ this.listType = this.configProps$.listType ? this.configProps$.listType : '';
229
+ this.hideLabel = this.configProps$.hideLabel;
230
+ this.parameters = this.configProps$?.parameters ? this.configProps$?.parameters : {};
231
+ this.referenceList = this.configProps$?.referenceList;
232
+ this.selectionKey = this.configProps$?.selectionKey;
233
+ this.primaryField = this.configProps$?.primaryField;
234
+ this.initialCaseClass = this.configProps$?.initialCaseClass;
235
+ this.showSecondaryInSearchOnly = this.configProps$?.showSecondaryInSearchOnly ? this.configProps$?.showSecondaryInSearchOnly : false;
236
+ this.isGroupData = this.configProps$?.isGroupData ? this.configProps$.isGroupData : false;
237
+ this.referenceType = this.configProps$?.referenceType;
238
+ this.secondaryFields = this.configProps$?.secondaryFields;
239
+ this.groupDataSource = this.configProps$?.groupDataSource ? this.configProps$?.groupDataSource : [];
240
+ this.matchPosition = this.configProps$?.matchPosition ? this.configProps$?.matchPosition : 'contains';
241
+ this.maxResultsDisplay = this.configProps$?.maxResultsDisplay;
242
+ this.groupColumnsConfig = this.configProps$?.groupColumnsConfig ? this.configProps$?.groupColumnsConfig : [{}];
243
+ this.selectionList = this.configProps$?.selectionList;
244
+ this.value$ = this.configProps$?.value;
245
+ }
246
+
247
+ // main search function trigger
248
+ getCaseListBasedOnParams(searchText, group, selectedRows, currentItemsTree, isTriggeredFromSearch = false) {
249
+ if (this.referenceList && this.referenceList.length > 0) {
250
+ this.listActions.getSelectedRows(true).then(result => {
251
+ selectedRows =
252
+ result.length > 0
253
+ ? result.map(item => {
254
+ return {
255
+ id: item[this.selectionKey.startsWith('.') ? this.selectionKey.substring(1) : this.selectionKey],
256
+ primary: item[this.primaryField.startsWith('.') ? this.primaryField.substring(1) : this.primaryField]
257
+ };
258
+ })
259
+ : [];
260
+ this.selectedItems = selectedRows;
261
+
262
+ const initalItemsTree = isTriggeredFromSearch || !currentItemsTree ? [...this.itemsTreeBaseData] : [...currentItemsTree];
263
+
264
+ doSearch(
265
+ searchText,
266
+ group,
267
+ this.initialCaseClass,
268
+ this.displayFieldMeta,
269
+ this.dataApiObj,
270
+ initalItemsTree,
271
+ this.isGroupData,
272
+ this.showSecondaryInSearchOnly,
273
+ selectedRows || []
274
+ ).then(res => {
275
+ this.itemsTree = res || [];
276
+ });
277
+ });
278
+ }
279
+ }
280
+
281
+ fieldOnChange(event: Event) {
282
+ this.value$ = (event.target as HTMLInputElement).value;
283
+ this.getCaseListBasedOnParams(this.value$, '', [...this.selectedItems], [...this.itemsTree], true);
284
+ }
285
+
286
+ optionChanged(event: MatAutocompleteSelectedEvent) {
287
+ this.angularPConnectData.actions?.onChange(this, event);
288
+ }
289
+
290
+ optionClicked = (event: Event, data: any): void => {
291
+ event.stopPropagation();
292
+ this.toggleSelection(data);
293
+ };
294
+
295
+ toggleSelection = (data: any): void => {
296
+ data.selected = !data.selected;
297
+ this.itemsTree.map((ele: any) => {
298
+ if (ele.id === data.id) {
299
+ ele.selected = data.selected;
300
+ }
301
+ return ele;
302
+ });
303
+
304
+ if (data.selected === true) {
305
+ this.selectedItems.push(data);
306
+ } else {
307
+ const index = this.selectedItems.findIndex(value => value.id === data.id);
308
+ this.selectedItems.splice(index, 1);
309
+ }
310
+
311
+ this.value$ = '';
312
+ // if this is a referenceList case
313
+ if (this.referenceList) this.setSelectedItemsForReferenceList(data);
314
+
315
+ this.getCaseListBasedOnParams(this.value$, '', [...this.selectedItems], [...this.itemsTree], true);
316
+ };
317
+
318
+ removeChip = (data: any): void => {
319
+ if (data) {
320
+ data = this.itemsTree.filter((ele: any) => {
321
+ return ele.id === data.id;
322
+ });
323
+ this.toggleSelection(data[0]);
324
+ }
325
+ };
326
+
327
+ setSelectedItemsForReferenceList(data: any) {
328
+ // Clear error messages if any
329
+ const propName = (this.pConn$.getStateProps() as any).selectionList;
330
+ this.pConn$.clearErrorMessages({
331
+ property: propName,
332
+ category: '',
333
+ context: ''
334
+ });
335
+ const { selected } = data;
336
+ if (selected) {
337
+ insertInstruction(this.pConn$, this.selectionList, this.selectionKey, this.primaryField, data);
338
+ } else {
339
+ deleteInstruction(this.pConn$, this.selectionList, this.selectionKey, data);
340
+ }
341
+ }
342
+
343
+ getErrorMessage() {
344
+ let errMessage = '';
345
+
346
+ // look for validation messages for json, pre-defined or just an error pushed from workitem (400)
347
+ if (this.fieldControl.hasError('message')) {
348
+ errMessage = this.angularPConnectData.validateMessage ?? '';
349
+ return errMessage;
350
+ }
351
+ if (this.fieldControl.hasError('required')) {
352
+ errMessage = 'You must enter a value';
353
+ } else if (this.fieldControl.errors) {
354
+ errMessage = this.fieldControl.errors.toString();
355
+ }
356
+
357
+ return errMessage;
358
+ }
359
+ }
@@ -0,0 +1,209 @@
1
+ import cloneDeep from 'lodash.clonedeep';
2
+
3
+ export function setVisibilityForList(c11nEnv, visibility) {
4
+ const { selectionMode, selectionList, renderMode, referenceList } = c11nEnv.getComponentConfig();
5
+ // usecase:multiselect, fieldgroup, editable table
6
+ if ((selectionMode === PCore.getConstants().LIST_SELECTION_MODE.MULTI && selectionList) || (renderMode === 'Editable' && referenceList)) {
7
+ c11nEnv.getListActions().setVisibility(visibility);
8
+ }
9
+ }
10
+
11
+ function preProcessColumns(columns) {
12
+ return columns?.map(col => {
13
+ const tempColObj = { ...col };
14
+ tempColObj.value = col.value && col.value.startsWith('.') ? col.value.substring(1) : col.value;
15
+ if (tempColObj.setProperty) {
16
+ tempColObj.setProperty = col.setProperty && col.setProperty.startsWith('.') ? col.setProperty.substring(1) : col.setProperty;
17
+ }
18
+ return tempColObj;
19
+ });
20
+ }
21
+
22
+ function getDisplayFieldsMetaData(columns) {
23
+ const displayColumns = columns?.filter(col => col.display === 'true');
24
+ const metaDataObj: any = {
25
+ key: '',
26
+ primary: '',
27
+ secondary: []
28
+ };
29
+ const keyCol = columns?.filter(col => col.key === 'true');
30
+ metaDataObj.key = keyCol?.length > 0 ? keyCol[0].value : 'auto';
31
+ const itemsRecordsColumn = columns?.filter(col => col.itemsRecordsColumn === 'true');
32
+ if (itemsRecordsColumn?.length > 0) {
33
+ metaDataObj.itemsRecordsColumn = itemsRecordsColumn[0].value;
34
+ }
35
+ const itemsGroupKeyColumn = columns?.filter(col => col.itemsGroupKeyColumn === 'true');
36
+ if (itemsGroupKeyColumn?.length > 0) {
37
+ metaDataObj.itemsGroupKeyColumn = itemsGroupKeyColumn[0].value;
38
+ }
39
+ for (let index = 0; index < displayColumns?.length; index += 1) {
40
+ if (displayColumns[index].secondary === 'true') {
41
+ metaDataObj.secondary.push(displayColumns[index].value);
42
+ } else if (displayColumns[index].primary === 'true') {
43
+ metaDataObj.primary = displayColumns[index].value;
44
+ }
45
+ }
46
+ return metaDataObj;
47
+ }
48
+
49
+ function createSingleTreeObejct(entry, displayFieldMeta, showSecondaryData, selected) {
50
+ const secondaryArr: any = [];
51
+ displayFieldMeta.secondary.forEach(col => {
52
+ secondaryArr.push(entry[col]);
53
+ });
54
+ const isSelected = selected.some(item => item.id === entry[displayFieldMeta.key]);
55
+
56
+ return {
57
+ id: entry[displayFieldMeta.key],
58
+ primary: entry[displayFieldMeta.primary],
59
+ secondary: showSecondaryData ? secondaryArr : [],
60
+ selected: isSelected
61
+ };
62
+ }
63
+
64
+ function putItemsDataInItemsTree(listObjData, displayFieldMeta, itemsTree, showSecondaryInSearchOnly, selected) {
65
+ let newTreeItems = itemsTree.slice();
66
+ const showSecondaryData = !showSecondaryInSearchOnly;
67
+ for (const obj of listObjData) {
68
+ const items = obj[displayFieldMeta.itemsRecordsColumn].map(entry => createSingleTreeObejct(entry, displayFieldMeta, showSecondaryData, selected));
69
+
70
+ newTreeItems = newTreeItems.map(caseObject => {
71
+ if (caseObject.id === obj[displayFieldMeta.itemsGroupKeyColumn]) {
72
+ caseObject.items = [...items];
73
+ }
74
+ return caseObject;
75
+ });
76
+ }
77
+ return newTreeItems;
78
+ }
79
+
80
+ function prepareSearchResults(listObjData, displayFieldMeta) {
81
+ const searchResults: any = [];
82
+ for (const obj of listObjData) {
83
+ searchResults.push(...obj[displayFieldMeta.itemsRecordsColumn]);
84
+ }
85
+ return searchResults;
86
+ }
87
+
88
+ async function doSearch(
89
+ searchText,
90
+ clickedGroup,
91
+ initialCaseClass,
92
+ displayFieldMeta,
93
+ dataApiObj, // deep clone of the dataApiObj
94
+ itemsTree,
95
+ isGroupData,
96
+ showSecondaryInSearchOnly,
97
+ selected
98
+ ) {
99
+ let searchTextForUngroupedData = '';
100
+ if (dataApiObj) {
101
+ // creating dataApiObject in grouped data cases
102
+ if (isGroupData) {
103
+ dataApiObj = cloneDeep(dataApiObj);
104
+ dataApiObj.fetchedNQData = false;
105
+ dataApiObj.cache = {};
106
+
107
+ // if we have no search text and no group selected, return the original tree
108
+ if (searchText === '' && clickedGroup === '') {
109
+ return itemsTree;
110
+ }
111
+
112
+ // setting the inital search text & search classes in ApiObject
113
+ dataApiObj.parameters[Object.keys(dataApiObj.parameters)[1]] = searchText;
114
+ dataApiObj.parameters[Object.keys(dataApiObj.parameters)[0]] = initialCaseClass;
115
+
116
+ // if we have a selected group
117
+ if (clickedGroup) {
118
+ // check if the data for this group is already present and no search text
119
+ if (searchText === '') {
120
+ const containsData = itemsTree.find(item => item.id === clickedGroup);
121
+ // do not make API call when items of respective group are already fetched
122
+ if (containsData?.items?.length) return itemsTree;
123
+ }
124
+
125
+ dataApiObj.parameters[Object.keys(dataApiObj.parameters)[0]] = JSON.stringify([clickedGroup]);
126
+ }
127
+ } else {
128
+ searchTextForUngroupedData = searchText;
129
+ }
130
+
131
+ // search API call
132
+ const response = await dataApiObj.fetchData(searchTextForUngroupedData).catch(() => {
133
+ return itemsTree;
134
+ });
135
+
136
+ let listObjData = response.data;
137
+ let newItemsTree = [];
138
+ if (isGroupData) {
139
+ if (searchText) {
140
+ listObjData = prepareSearchResults(listObjData, displayFieldMeta);
141
+ } else {
142
+ newItemsTree = putItemsDataInItemsTree(listObjData, displayFieldMeta, itemsTree, showSecondaryInSearchOnly, selected);
143
+ return newItemsTree;
144
+ }
145
+ }
146
+ const showSecondaryData = showSecondaryInSearchOnly ? !!searchText : true;
147
+ if (listObjData !== undefined && listObjData.length > 0) {
148
+ newItemsTree = listObjData.map(entry => createSingleTreeObejct(entry, displayFieldMeta, showSecondaryData, selected));
149
+ }
150
+ return newItemsTree;
151
+ }
152
+
153
+ return itemsTree;
154
+ }
155
+
156
+ function setValuesToPropertyList(searchText, assocProp, items, columns, actions, updatePropertyInRedux = true) {
157
+ const setPropertyList = columns
158
+ ?.filter(col => col.setProperty)
159
+ .map(col => {
160
+ return {
161
+ source: col.value,
162
+ target: col.setProperty,
163
+ key: col.key,
164
+ primary: col.primary
165
+ };
166
+ });
167
+ const valueToSet: any = [];
168
+ if (setPropertyList.length > 0) {
169
+ setPropertyList.forEach(prop => {
170
+ items.forEach(item => {
171
+ if (prop.key === 'true' && item) {
172
+ valueToSet.push(item.id);
173
+ } else if (prop.primary === 'true' || !item) {
174
+ valueToSet.push(searchText);
175
+ }
176
+ });
177
+
178
+ if (updatePropertyInRedux) {
179
+ // BUG-666851 setting options so that the store values are replaced and not merged
180
+ const options = {
181
+ isArrayDeepMerge: false
182
+ };
183
+ if (prop.target === 'Associated property') {
184
+ actions.updateFieldValue(assocProp, valueToSet, options);
185
+ } else {
186
+ actions.updateFieldValue(`.${prop.target}`, valueToSet, options);
187
+ }
188
+ }
189
+ });
190
+ }
191
+ return valueToSet;
192
+ }
193
+
194
+ function getGroupDataForItemsTree(groupDataSource, groupsDisplayFieldMeta, showSecondaryInSearchOnly) {
195
+ return groupDataSource?.map(group => {
196
+ const secondaryArr: any = [];
197
+ groupsDisplayFieldMeta.secondary.forEach(col => {
198
+ secondaryArr.push(group[col]);
199
+ });
200
+ return {
201
+ id: group[groupsDisplayFieldMeta.key],
202
+ primary: group[groupsDisplayFieldMeta.primary],
203
+ secondary: showSecondaryInSearchOnly ? [] : secondaryArr,
204
+ items: []
205
+ };
206
+ });
207
+ }
208
+
209
+ export { preProcessColumns, getDisplayFieldsMetaData, doSearch, setValuesToPropertyList, getGroupDataForItemsTree };
@@ -2,22 +2,33 @@
2
2
  <component-mapper *ngIf="bVisible$ !== false" name="FieldValueList" [props]="{ label$, value$, displayMode$ }"></component-mapper>
3
3
  </div>
4
4
  <ng-template #noDisplayMode>
5
- <div *ngIf="!bReadonly$ && bHasForm$; else noEdit">
5
+ <div *ngIf="bHasForm$; else noEdit">
6
6
  <div [formGroup]="formGroup$" *ngIf="bVisible$">
7
7
  <mat-form-field class="psdk-full-width" subscriptSizing="dynamic" [hintLabel]="helperText">
8
8
  <mat-label>{{ label$ }}</mat-label>
9
- <!-- <span matPrefix>% &nbsp;</span> -->
10
9
  <input
10
+ type="text"
11
11
  matInput
12
+ currencyMask
13
+ [options]="{
14
+ prefix: '',
15
+ suffix: '%',
16
+ thousands: currSep,
17
+ decimal: currDec,
18
+ align: 'left',
19
+ nullable: true,
20
+ precision: decimalPrecision,
21
+ inputMode: inputMode
22
+ }"
12
23
  [placeholder]="placeholder"
13
- type="number"
14
24
  step=".01"
15
- [value]="value$"
25
+ [formControlName]="controlName$"
16
26
  [required]="bRequired$"
17
27
  [formControl]="fieldControl"
18
28
  [attr.data-test-id]="testId"
19
29
  (change)="fieldOnChange($event)"
20
30
  (blur)="fieldOnBlur($event)"
31
+ [readonly]="bReadonly$"
21
32
  />
22
33
  <mat-error *ngIf="fieldControl.invalid">{{ getErrorMessage() }}</mat-error>
23
34
  </mat-form-field>
@@ -4,12 +4,17 @@ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
4
4
  import { MatInputModule } from '@angular/material/input';
5
5
  import { MatFormFieldModule } from '@angular/material/form-field';
6
6
  import { interval } from 'rxjs';
7
+ import { NgxCurrencyDirective, NgxCurrencyInputMode } from 'ngx-currency';
7
8
  import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
8
9
  import { Utils } from '@pega/angular-sdk-components';
9
10
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
11
+ import { handleEvent } from '@pega/angular-sdk-components';
12
+ import { getCurrencyCharacters } from '@pega/angular-sdk-components';
10
13
  import { PConnFieldProps } from '@pega/angular-sdk-components';
11
14
 
12
15
  interface PercentageProps extends PConnFieldProps {
16
+ showGroupSeparators?: string;
17
+ decimalPrecision?: number;
13
18
  // If any, enter additional props that only exist on Percentage here
14
19
  }
15
20
 
@@ -18,7 +23,7 @@ interface PercentageProps extends PConnFieldProps {
18
23
  templateUrl: './percentage.component.html',
19
24
  styleUrls: ['./percentage.component.scss'],
20
25
  standalone: true,
21
- imports: [CommonModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, forwardRef(() => ComponentMapperComponent)]
26
+ imports: [CommonModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, NgxCurrencyDirective, forwardRef(() => ComponentMapperComponent)]
22
27
  })
23
28
  export class PercentageComponent implements OnInit, OnDestroy {
24
29
  @Input() pConn$: typeof PConnect;
@@ -41,7 +46,10 @@ export class PercentageComponent implements OnInit, OnDestroy {
41
46
  testId: string;
42
47
  helperText: string;
43
48
  placeholder: string;
44
-
49
+ currDec: string;
50
+ currSep: string;
51
+ inputMode: any;
52
+ decimalPrecision: number | undefined;
45
53
  fieldControl = new FormControl<number | null>(null, null);
46
54
 
47
55
  constructor(
@@ -104,6 +112,7 @@ export class PercentageComponent implements OnInit, OnDestroy {
104
112
  this.testId = this.configProps$.testId;
105
113
  this.label$ = this.configProps$.label;
106
114
  this.displayMode$ = this.configProps$.displayMode;
115
+ this.inputMode = NgxCurrencyInputMode.Natural;
107
116
  let nValue: any = this.configProps$.value;
108
117
  if (nValue) {
109
118
  if (typeof nValue === 'string') {
@@ -113,6 +122,11 @@ export class PercentageComponent implements OnInit, OnDestroy {
113
122
  }
114
123
  this.helperText = this.configProps$.helperText;
115
124
  this.placeholder = this.configProps$.placeholder || '';
125
+ const showGroupSeparators = this.configProps$.showGroupSeparators;
126
+
127
+ const theSymbols = getCurrencyCharacters('');
128
+ this.currDec = theSymbols.theDecimalIndicator || '2';
129
+ this.currSep = showGroupSeparators ? theSymbols.theDigitGroupSeparator : '';
116
130
 
117
131
  // timeout and detectChanges to avoid ExpressionChangedAfterItHasBeenCheckedError
118
132
  setTimeout(() => {
@@ -141,6 +155,8 @@ export class PercentageComponent implements OnInit, OnDestroy {
141
155
  this.bReadonly$ = this.utils.getBooleanValue(this.configProps$.readOnly);
142
156
  }
143
157
 
158
+ this.decimalPrecision = this.configProps$?.decimalPrecision ?? 2;
159
+
144
160
  this.componentReference = (this.pConn$.getStateProps() as any).value;
145
161
 
146
162
  // trigger display of error message with field control
@@ -158,9 +174,17 @@ export class PercentageComponent implements OnInit, OnDestroy {
158
174
  }
159
175
 
160
176
  fieldOnBlur(event: any) {
161
- // PConnect wants to use eventHandler for onBlur
162
-
163
- this.angularPConnectData.actions?.onBlur(this, event);
177
+ const actionsApi = this.pConn$?.getActionsApi();
178
+ const propName = (this.pConn$?.getStateProps() as any).value;
179
+ let value = event?.target?.value;
180
+ value = value ? value.replace(/%/g, '') : '';
181
+ if (this.currSep === ',') {
182
+ value = value.replace(/,/g, '');
183
+ } else {
184
+ value = value?.replace(/\./g, '');
185
+ value = value?.replace(/,/g, '.');
186
+ }
187
+ handleEvent(actionsApi, 'changeNblur', propName, value);
164
188
  }
165
189
 
166
190
  getErrorMessage() {