@pega/angular-sdk-overrides 25.1.13 → 26.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 (153) hide show
  1. package/lib/designSystemExtension/alert/alert.component.html +4 -1
  2. package/lib/designSystemExtension/alert/alert.component.scss +9 -10
  3. package/lib/designSystemExtension/alert/alert.component.ts +6 -2
  4. package/lib/designSystemExtension/field-group/field-group.component.ts +11 -3
  5. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.html +51 -36
  6. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.scss +12 -0
  7. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.ts +11 -0
  8. package/lib/designSystemExtension/operator/operator.component.html +5 -3
  9. package/lib/designSystemExtension/operator/operator.component.scss +7 -0
  10. package/lib/designSystemExtension/operator/operator.component.ts +2 -1
  11. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.html +100 -10
  12. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.scss +141 -1
  13. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.spec.ts +1 -1
  14. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.ts +240 -72
  15. package/lib/field/auto-complete/auto-complete.component.html +46 -5
  16. package/lib/field/auto-complete/auto-complete.component.scss +92 -0
  17. package/lib/field/auto-complete/auto-complete.component.ts +350 -23
  18. package/lib/field/cancel-alert/cancel-alert.component.ts +38 -15
  19. package/lib/field/check-box/check-box.component.html +8 -6
  20. package/lib/field/check-box/check-box.component.ts +9 -2
  21. package/lib/field/currency/currency.component.html +2 -1
  22. package/lib/field/currency/currency.component.ts +10 -1
  23. package/lib/field/date/date.component.html +2 -1
  24. package/lib/field/date/date.component.ts +2 -0
  25. package/lib/field/date-time/date-time.component.html +2 -1
  26. package/lib/field/date-time/date-time.component.scss +46 -40
  27. package/lib/field/date-time/date-time.component.ts +29 -0
  28. package/lib/field/decimal/decimal.component.html +2 -1
  29. package/lib/field/decimal/decimal.component.ts +2 -0
  30. package/lib/field/dropdown/dropdown.component.html +4 -1
  31. package/lib/field/dropdown/dropdown.component.ts +18 -19
  32. package/lib/field/email/email.component.html +2 -1
  33. package/lib/field/email/email.component.ts +2 -2
  34. package/lib/field/embedded-data-multi/embedded-data-multi.component.html +3 -0
  35. package/lib/field/embedded-data-multi/embedded-data-multi.component.scss +0 -0
  36. package/lib/field/embedded-data-multi/embedded-data-multi.component.ts +173 -0
  37. package/lib/field/field.base.ts +27 -1
  38. package/lib/field/group/group.component.html +1 -1
  39. package/lib/field/group/group.component.ts +15 -5
  40. package/lib/field/integer/integer.component.html +2 -1
  41. package/lib/field/integer/integer.component.ts +4 -2
  42. package/lib/field/location/location.component.html +2 -1
  43. package/lib/field/location/location.component.ts +3 -1
  44. package/lib/field/multiselect/multiselect.component.html +1 -1
  45. package/lib/field/multiselect/multiselect.component.ts +23 -4
  46. package/lib/field/multiselect/utils.ts +5 -3
  47. package/lib/field/object-reference/object-reference.component.html +30 -15
  48. package/lib/field/object-reference/object-reference.component.ts +818 -94
  49. package/lib/field/percentage/percentage.component.html +2 -1
  50. package/lib/field/percentage/percentage.component.ts +10 -1
  51. package/lib/field/phone/phone.component.html +2 -1
  52. package/lib/field/phone/phone.component.ts +2 -1
  53. package/lib/field/radio-buttons/radio-buttons.component.html +2 -1
  54. package/lib/field/radio-buttons/radio-buttons.component.ts +10 -1
  55. package/lib/field/rich-text/rich-text.component.scss +1 -166
  56. package/lib/field/rich-text/rich-text.component.spec.ts +1 -1
  57. package/lib/field/rich-text/rich-text.component.ts +1 -1
  58. package/lib/field/selectable-card/selectable-card.component.html +1 -0
  59. package/lib/field/selectable-card/selectable-card.component.ts +31 -16
  60. package/lib/field/semantic-link/semantic-link.component.html +1 -1
  61. package/lib/field/semantic-link/semantic-link.component.scss +4 -0
  62. package/lib/field/semantic-link/semantic-link.component.ts +13 -7
  63. package/lib/field/text-area/text-area.component.html +2 -1
  64. package/lib/field/text-area/text-area.component.ts +2 -1
  65. package/lib/field/text-input/text-input.component.html +2 -1
  66. package/lib/field/text-input/text-input.component.ts +2 -1
  67. package/lib/field/time/time.component.html +2 -1
  68. package/lib/field/time/time.component.ts +2 -1
  69. package/lib/field/url/url.component.html +2 -1
  70. package/lib/field/url/url.component.ts +2 -1
  71. package/lib/field/user-reference/user-reference.component.html +4 -2
  72. package/lib/field/user-reference/user-reference.component.ts +11 -6
  73. package/lib/infra/Containers/flow-container/flow-container.component.html +1 -1
  74. package/lib/infra/Containers/flow-container/flow-container.component.ts +35 -46
  75. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.html +12 -0
  76. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.spec.ts +130 -0
  77. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.ts +77 -0
  78. package/lib/infra/Containers/modal-view-container/modal-view-container.component.html +35 -8
  79. package/lib/infra/Containers/modal-view-container/modal-view-container.component.ts +185 -194
  80. package/lib/infra/Containers/view-container/view-container.component.ts +32 -35
  81. package/lib/infra/assignment/assignment.component.html +2 -1
  82. package/lib/infra/assignment/assignment.component.ts +42 -15
  83. package/lib/infra/defer-load/defer-load.component.ts +6 -3
  84. package/lib/infra/multi-step/multi-step.component.html +25 -33
  85. package/lib/infra/multi-step/multi-step.component.scss +67 -125
  86. package/lib/infra/multi-step/multi-step.component.ts +11 -0
  87. package/lib/infra/navbar/navbar.component.ts +33 -36
  88. package/lib/infra/reference/reference.component.ts +1 -2
  89. package/lib/infra/root-container/root-container.component.ts +33 -38
  90. package/lib/infra/view/view.component.ts +21 -40
  91. package/lib/template/advanced-search/advanced-search.component.ts +0 -1
  92. package/lib/template/advanced-search/search-groups/search-groups.component.ts +2 -3
  93. package/lib/template/app-shell/app-shell.component.ts +13 -13
  94. package/lib/template/base/details-template-base.ts +41 -14
  95. package/lib/template/case-summary/case-summary.component.ts +15 -1
  96. package/lib/template/case-view/case-view.component.html +13 -1
  97. package/lib/template/case-view/case-view.component.spec.ts +119 -10
  98. package/lib/template/case-view/case-view.component.ts +52 -3
  99. package/lib/template/data-reference/data-reference.component.ts +2 -4
  100. package/lib/template/data-reference/search-form/search-form.component.html +1 -1
  101. package/lib/template/data-reference/search-form/search-form.component.ts +9 -1
  102. package/lib/template/data-reference/search-form/tabsData.ts +0 -1
  103. package/lib/template/default-form/default-form.component.html +19 -3
  104. package/lib/template/default-form/default-form.component.ts +13 -3
  105. package/lib/template/details/details.component.html +23 -3
  106. package/lib/template/details/details.component.scss +9 -2
  107. package/lib/template/details/details.component.ts +3 -70
  108. package/lib/template/details-narrow-wide/details-narrow-wide.component.html +21 -2
  109. package/lib/template/details-narrow-wide/details-narrow-wide.component.scss +8 -1
  110. package/lib/template/details-narrow-wide/details-narrow-wide.component.ts +3 -39
  111. package/lib/template/details-one-column/details-one-column.component.html +23 -1
  112. package/lib/template/details-one-column/details-one-column.component.scss +14 -0
  113. package/lib/template/details-one-column/details-one-column.component.ts +3 -40
  114. package/lib/template/details-three-column/details-three-column.component.html +21 -2
  115. package/lib/template/details-three-column/details-three-column.component.scss +8 -1
  116. package/lib/template/details-three-column/details-three-column.component.ts +3 -48
  117. package/lib/template/details-two-column/details-two-column.component.html +21 -2
  118. package/lib/template/details-two-column/details-two-column.component.scss +8 -1
  119. package/lib/template/details-two-column/details-two-column.component.ts +3 -46
  120. package/lib/template/details-wide-narrow/details-wide-narrow.component.html +21 -2
  121. package/lib/template/details-wide-narrow/details-wide-narrow.component.scss +8 -1
  122. package/lib/template/details-wide-narrow/details-wide-narrow.component.ts +3 -43
  123. package/lib/template/field-group-template/field-group-template.component.ts +19 -6
  124. package/lib/template/field-value-list/field-value-list.component.scss +17 -0
  125. package/lib/template/list-view/list-view.component.ts +47 -11
  126. package/lib/template/self-service-case-view/self-service-case-view.component.html +13 -1
  127. package/lib/template/self-service-case-view/self-service-case-view.component.spec.ts +96 -11
  128. package/lib/template/self-service-case-view/self-service-case-view.component.ts +52 -3
  129. package/lib/template/simple-table/simple-table.component.ts +6 -0
  130. package/lib/template/simple-table-manual/helpers.ts +84 -16
  131. package/lib/template/simple-table-manual/simple-table-manual.component.html +30 -14
  132. package/lib/template/simple-table-manual/simple-table-manual.component.scss +8 -2
  133. package/lib/template/simple-table-manual/simple-table-manual.component.ts +131 -43
  134. package/lib/template/single-reference-readonly/single-reference-readonly.component.html +0 -0
  135. package/lib/template/single-reference-readonly/single-reference-readonly.component.scss +2 -3
  136. package/lib/template/single-reference-readonly/single-reference-readonly.component.ts +7 -8
  137. package/lib/template/wss-nav-bar/wss-nav-bar.component.ts +19 -21
  138. package/lib/widget/attachment/AttachmentUtils.ts +19 -11
  139. package/lib/widget/attachment/attachment.component.html +16 -6
  140. package/lib/widget/attachment/attachment.component.ts +14 -4
  141. package/lib/widget/case-history/case-history.component.ts +6 -2
  142. package/lib/widget/feed-container/feed-container.component.ts +1 -199
  143. package/lib/widget/file-utility/file-utility.component.ts +26 -30
  144. package/lib/widget/todo/todo.component.ts +7 -2
  145. package/package.json +2 -2
  146. package/lib/designSystemExtension/material-details/material-details.component.html +0 -40
  147. package/lib/designSystemExtension/material-details/material-details.component.scss +0 -116
  148. package/lib/designSystemExtension/material-details/material-details.component.spec.ts +0 -24
  149. package/lib/designSystemExtension/material-details/material-details.component.ts +0 -31
  150. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.html +0 -29
  151. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.scss +0 -34
  152. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.spec.ts +0 -22
  153. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.ts +0 -37
@@ -1,17 +1,32 @@
1
- import { Component, OnInit, Input, Output, EventEmitter, NgZone, forwardRef, OnDestroy } from '@angular/core';
1
+ import { ChangeDetectorRef, Component, OnInit, Input, Output, EventEmitter, forwardRef, OnDestroy } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import { FormBuilder, FormGroup } from '@angular/forms';
4
- import isEqual from 'fast-deep-equal';
5
4
  import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
6
5
  import { ProgressSpinnerService } from '@pega/angular-sdk-components';
6
+ import { BannerService } from '@pega/angular-sdk-components';
7
7
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
8
- import { getBanners } from '@pega/angular-sdk-components';
9
8
 
10
9
  /**
11
10
  * WARNING: This file is part of the infrastructure component responsible for working with Redux and managing the creation and update of Redux containers and PConnect.
12
11
  * You may override Material components within this component if needed, but do not modify any container-related logic. Changing this logic can lead to unexpected behavior.
13
12
  */
14
13
 
14
+ // One entry per open modal — supports stacking (e.g. "Create new" opened from within another modal)
15
+ interface ModalEntry {
16
+ key: string;
17
+ title: string;
18
+ createdViewPConn$: any;
19
+ arChildren$: any[];
20
+ isMultiRecordData: boolean;
21
+ isDataObjectModal: boolean;
22
+ dataRecordKeys: string;
23
+ dataObjectActionID: string;
24
+ dataObjectAction: string;
25
+ dataObjectClassId: string;
26
+ context: string;
27
+ updateToken: number;
28
+ }
29
+
15
30
  @Component({
16
31
  selector: 'app-modal-view-container',
17
32
  templateUrl: './modal-view-container.component.html',
@@ -27,41 +42,33 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
27
42
  // Used with AngularPConnect
28
43
  angularPConnectData: AngularPConnectData = {};
29
44
 
30
- arChildren$: any[];
31
45
  stateProps$: object;
32
- banners: any;
33
- templateName$: string;
34
- buildName$: string;
35
- context$: string;
36
- title$ = '';
37
- bShowModal$ = false;
38
- itemKey$: string;
39
46
  formGroup$: FormGroup;
40
- oCaseInfo: object = {};
41
-
42
- // for causing a change on assignment
43
- updateToken$ = 0;
44
47
 
45
48
  routingInfoRef: any = {};
46
49
 
47
- // created object is now a View with a Template
48
- // Use its PConnect to render the CaseView; DON'T replace this.pConn$
49
- createdViewPConn$: any;
50
+ // Stack of open modals — supports one modal opening another (e.g. "Create new" from within a modal).
51
+ // Keyed the same as PCore's routingInfo.items so open/update/close can be derived from accessedOrder.
52
+ modalStack: ModalEntry[] = [];
53
+ private modalCollection: Record<string, object> = {};
50
54
 
51
55
  bSubscribed = false;
52
56
  cancelPConn$?: typeof PConnect;
57
+ cancelHideDelete$: boolean;
58
+ cancelIsDataObject$: boolean;
59
+ cancelSkipReleaseLockRequest$: any;
53
60
  bShowCancelAlert$ = false;
54
61
  bAlertState: boolean;
55
62
  localizedVal: Function;
56
63
  localeCategory = 'Data Object';
57
- isMultiRecord = false;
58
64
  actionsDialog = false;
59
65
 
60
66
  constructor(
61
67
  private angularPConnect: AngularPConnectService,
62
- private ngZone: NgZone,
68
+ private cdRef: ChangeDetectorRef,
63
69
  private psService: ProgressSpinnerService,
64
- private fb: FormBuilder
70
+ private fb: FormBuilder,
71
+ public bannerService: BannerService
65
72
  ) {
66
73
  // create the formGroup
67
74
  this.formGroup$ = fb.group({ hideRequired: false });
@@ -71,22 +78,12 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
71
78
  // First thing in initialization is registering and subscribing to the AngularPConnect service
72
79
  this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
73
80
 
74
- const baseContext = this.pConn$.getContextName();
75
- const acName = this.pConn$.getContainerName();
76
-
77
- // for now, in general this should be overridden by updateSelf(), and not be blank
78
- if (this.itemKey$ === '') {
79
- this.itemKey$ = baseContext.concat('/').concat(acName);
80
- }
81
-
82
81
  const containerMgr = this.pConn$.getContainerManager();
83
82
 
84
83
  containerMgr.initializeContainers({
85
84
  type: 'multiple'
86
85
  });
87
86
 
88
- // const { CONTAINER_TYPE, PUB_SUB_EVENTS } = PCore.getConstants();
89
-
90
87
  this.angularPConnect.shouldComponentUpdate(this);
91
88
  this.localizedVal = PCore.getLocaleUtils().getLocaleValue;
92
89
  }
@@ -114,10 +111,6 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
114
111
  // ONLY call updateSelf when the component should update
115
112
  if (bUpdateSelf) {
116
113
  this.updateSelf();
117
- } else if (this.bShowModal$) {
118
- // right now onlu get one updated when initial diaplay. So, once modal is up
119
- // let fall through and do a check with "compareCaseInfoIsDifferent" until fixed
120
- // this.updateSelf();
121
114
  }
122
115
  }
123
116
 
@@ -134,143 +127,102 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
134
127
  } catch (ex) {
135
128
  console.log(ex);
136
129
  }
137
- // const configProps = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps());
138
130
  this.stateProps$ = this.pConn$.getStateProps();
139
- this.banners = this.getBanners();
140
131
 
141
- if (!loadingInfo) {
142
- // turn off spinner
143
- // this.psService.sendMessage(false);
132
+ if (!routingInfo || loadingInfo) {
133
+ return;
144
134
  }
145
135
 
146
- if (routingInfo && !loadingInfo /* && this.bUpdate */) {
147
- const currentOrder = routingInfo.accessedOrder;
136
+ const { accessedOrder, type } = routingInfo;
148
137
 
149
- if (undefined == currentOrder) {
150
- return;
151
- }
152
-
153
- const currentItems = routingInfo.items;
154
-
155
- const { key, latestItem } = this.getKeyAndLatestItem(routingInfo);
156
-
157
- if (currentOrder.length > 0) {
158
- if (currentItems[key] && currentItems[key].view && Object.keys(currentItems[key].view).length > 0) {
159
- const currentItem = currentItems[key];
160
- const rootView = currentItem.view;
161
- const { context } = rootView.config;
162
- const config: any = { meta: rootView };
163
- config.options = {
164
- context: currentItem.context,
165
- hasForm: true,
166
- pageReference: context || this.pConn$.getPageReference()
167
- };
168
-
169
- if (!this.bSubscribed) {
170
- this.bSubscribed = true;
171
- const { PUB_SUB_EVENTS } = PCore.getConstants();
172
- PCore.getPubSubUtils().subscribe(
173
- PUB_SUB_EVENTS.EVENT_SHOW_CANCEL_ALERT,
174
- payload => {
175
- this.showAlert(payload);
176
- },
177
- PUB_SUB_EVENTS.EVENT_SHOW_CANCEL_ALERT
178
- );
179
- }
180
-
181
- // let configObject = PCore.createPConnect(config);
182
-
183
- // THIS is where the ViewContainer creates a View
184
- // The config has meta.config.type = "view"
185
- this.createView(routingInfo, currentItem, latestItem, key);
186
- }
187
- } else {
188
- this.hideModal();
189
- }
138
+ if (undefined == accessedOrder) {
139
+ return;
190
140
  }
191
- }
192
141
 
193
- createView(routingInfo, currentItem, latestItem, key) {
194
- const configObject = this.getConfigObject(currentItem, null, false);
195
- const newComp = configObject?.getPConnect();
196
- // const newCompName = newComp.getComponentName();
197
- const caseInfo = newComp && newComp.getDataObject() && newComp.getDataObject().caseInfo ? newComp.getDataObject().caseInfo : null;
198
- // The metadata for pyDetails changed such that the "template": "CaseView"
199
- // is no longer a child of the created View but is in the created View's
200
- // config. So, we DON'T want to replace this.pConn$ since the created
201
- // component is a View (and not a ViewContainer). We now look for the
202
- // "template" type directly in the created component (newComp) and NOT
203
- // as a child of the newly created component.
204
- // console.log(`---> ModalViewContainer created new ${newCompName}`);
205
-
206
- // Use the newly created component (View) info but DO NOT replace
207
- // this ModalViewContainer's pConn$, etc.
208
- // Note that we're now using the newly created View's PConnect in the
209
- // ViewContainer HTML template to guide what's rendered similar to what
210
- // the Nebula/Constellation return of React.Fragment does
211
-
212
- // right now need to check caseInfo for changes, to trigger redraw, not getting
213
- // changes from angularPconnect except for first draw
214
- if (newComp && caseInfo && this.compareCaseInfoIsDifferent(caseInfo)) {
215
- this.psService.sendMessage(false);
216
-
217
- this.ngZone.run(() => {
218
- this.createdViewPConn$ = newComp;
219
- const newConfigProps = newComp.getConfigProps();
220
- this.templateName$ = 'template' in newConfigProps ? (newConfigProps.template as string) : '';
221
-
222
- const { actionName } = latestItem;
223
- const theNewCaseInfo = newComp.getCaseInfo();
224
- // const caseName = theNewCaseInfo.getName();
225
- const ID = theNewCaseInfo.getBusinessID() || theNewCaseInfo.getID();
226
-
227
- const caseTypeName = theNewCaseInfo.getCaseTypeName();
228
- const isDataObject = routingInfo.items[latestItem.context].resourceType === PCore.getConstants().RESOURCE_TYPES.DATA;
229
- const dataObjectAction = routingInfo.items[latestItem.context].resourceStatus;
230
- this.isMultiRecord = routingInfo.items[latestItem.context].isMultiRecordData;
231
- this.context$ = latestItem.context;
232
- this.title$ =
233
- isDataObject || this.isMultiRecord
234
- ? this.getModalHeading(dataObjectAction)
235
- : this.determineModalHeaderByAction(actionName, caseTypeName, ID, this.createdViewPConn$?.getCaseLocaleReference());
236
-
237
- const bIsRefComponent = this.checkIfRefComponent(newComp);
238
-
239
- if (bIsRefComponent) {
240
- this.arChildren$ = [newComp.getComponent()];
241
- } else {
242
- // update children with new view's children
243
- this.arChildren$ = newComp.getChildren();
142
+ const { MULTIPLE } = PCore.getConstants().CONTAINER_TYPE;
143
+ const { key, latestItem } = this.getKeyAndLatestItem(routingInfo);
144
+
145
+ if (latestItem && type === MULTIPLE && (this.isOpenModalAction(accessedOrder) || this.isUpdateModalAction(accessedOrder))) {
146
+ const currentItem = routingInfo.items[key];
147
+ if (currentItem?.view && Object.keys(currentItem.view).length > 0) {
148
+ if (!this.bSubscribed) {
149
+ this.bSubscribed = true;
150
+ const { PUB_SUB_EVENTS } = PCore.getConstants();
151
+ PCore.getPubSubUtils().subscribe(
152
+ PUB_SUB_EVENTS.EVENT_SHOW_CANCEL_ALERT,
153
+ payload => {
154
+ this.showAlert(payload);
155
+ },
156
+ PUB_SUB_EVENTS.EVENT_SHOW_CANCEL_ALERT
157
+ );
244
158
  }
245
159
 
246
- this.bShowModal$ = true;
247
-
248
- // for when non modal
249
- this.modalVisibleChange.emit(this.bShowModal$);
250
-
251
- // save off itemKey to be used for finishAssignment, etc.
252
- this.itemKey$ = key;
253
-
254
- // cause a change for assignment
255
- this.updateToken$ = new Date().getTime();
256
- });
160
+ this.upsertModal(routingInfo, latestItem, key, accessedOrder);
161
+ }
162
+ } else if (this.isCloseModalAction(accessedOrder)) {
163
+ this.handleModalClose(accessedOrder);
257
164
  }
258
165
  }
259
166
 
260
- hideModal() {
261
- if (this.bShowModal$) {
262
- // other code in Nebula/Constellation not needed currently, but if so later,
263
- // should put here
167
+ // Builds/refreshes one modal-stack entry and pushes or updates it in-place
168
+ upsertModal(routingInfo, latestItem, key, accessedOrder) {
169
+ const entry = this.buildModalEntry(routingInfo, latestItem, key);
170
+
171
+ if (this.isUpdateModalAction(accessedOrder)) {
172
+ this.modalStack = this.modalStack.map(modal => (modal.key === key ? entry : modal));
173
+ } else if (this.isOpenModalAction(accessedOrder)) {
174
+ this.handleModalOpen(key);
175
+ this.modalStack = [...this.modalStack, entry];
264
176
  }
265
177
 
266
- this.ngZone.run(() => {
267
- this.bShowModal$ = false;
178
+ this.psService.sendMessage(false);
179
+ this.modalVisibleChange.emit(this.modalStack.length > 0);
180
+ this.cdRef.markForCheck();
181
+ }
268
182
 
269
- // for when non modal
270
- this.modalVisibleChange.emit(this.bShowModal$);
183
+ buildModalEntry(routingInfo, latestItem, key): ModalEntry {
184
+ const configObject = this.getConfigObject(latestItem, null, false);
185
+ // latestItem is only reached once its view metadata is populated (see updateSelf), so the
186
+ // created component is always present here — same assumption the old single-modal code made.
187
+ const newComp = configObject!.getPConnect();
188
+
189
+ const { actionName } = latestItem;
190
+ const theCaseInfo = newComp.getCaseInfo();
191
+ const ID = theCaseInfo.getBusinessID() || theCaseInfo.getID();
192
+ const caseTypeName = theCaseInfo.getCaseTypeName();
193
+
194
+ const isDataObject = routingInfo.items[latestItem.context].resourceType === PCore.getConstants().RESOURCE_TYPES.DATA;
195
+ const dataObjectAction = routingInfo.items[latestItem.context].resourceStatus;
196
+ const isMultiRecordData = routingInfo.items[latestItem.context].isMultiRecordData;
197
+
198
+ const title = this.getHeadingValue(
199
+ latestItem,
200
+ isMultiRecordData,
201
+ isDataObject,
202
+ actionName,
203
+ dataObjectAction,
204
+ caseTypeName,
205
+ ID,
206
+ newComp?.getCaseLocaleReference()
207
+ );
271
208
 
272
- this.oCaseInfo = {};
273
- });
209
+ const bIsRefComponent = this.checkIfRefComponent(newComp);
210
+ const arChildren$ = bIsRefComponent ? [newComp.getComponent()] : newComp.getChildren();
211
+
212
+ return {
213
+ key,
214
+ title,
215
+ createdViewPConn$: newComp,
216
+ arChildren$,
217
+ isMultiRecordData,
218
+ isDataObjectModal: isDataObject && !isMultiRecordData,
219
+ dataRecordKeys: latestItem.key || '',
220
+ dataObjectActionID: routingInfo.items[latestItem.context].actionID || '',
221
+ dataObjectAction: dataObjectAction || '',
222
+ dataObjectClassId: newComp.getValue('.classID') || '',
223
+ context: latestItem.context,
224
+ updateToken: new Date().getTime()
225
+ };
274
226
  }
275
227
 
276
228
  getConfigObject(item, pConnect, isReverseCoexistence = false) {
@@ -317,13 +269,17 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
317
269
  this.bAlertState = bData;
318
270
  this.bShowCancelAlert$ = false;
319
271
  if (this.bAlertState) {
320
- this.hideModal();
272
+ // Discard confirmed — matches react-sdk: drop the whole stack rather than just the top entry
273
+ this.modalCollection = {};
274
+ this.modalStack = [];
275
+ this.modalVisibleChange.emit(false);
276
+ this.cdRef.markForCheck();
321
277
  }
322
278
  }
323
279
 
324
280
  showAlert(payload) {
325
281
  const { latestItem } = this.getKeyAndLatestItem(this.routingInfoRef.current);
326
- const { isModalAction } = payload;
282
+ const { isModalAction, hideDelete, isDataObject, skipReleaseLockRequest } = payload;
327
283
 
328
284
  /*
329
285
  If we are in create stage full page mode, created a new case and trying to click on cancel button
@@ -331,10 +287,12 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
331
287
  */
332
288
  if (latestItem && isModalAction && !this.actionsDialog) {
333
289
  const configObject = this.getConfigObject(latestItem, this.pConn$);
334
- this.ngZone.run(() => {
335
- this.cancelPConn$ = configObject?.getPConnect();
336
- this.bShowCancelAlert$ = true;
337
- });
290
+ this.cancelPConn$ = configObject?.getPConnect();
291
+ this.cancelHideDelete$ = hideDelete;
292
+ this.cancelIsDataObject$ = isDataObject;
293
+ this.cancelSkipReleaseLockRequest$ = skipReleaseLockRequest;
294
+ this.bShowCancelAlert$ = true;
295
+ this.cdRef.markForCheck();
338
296
  }
339
297
  }
340
298
 
@@ -356,37 +314,62 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
356
314
  return {};
357
315
  }
358
316
 
359
- compareCaseInfoIsDifferent(oCurrentCaseInfo: object): boolean {
360
- let bRet = false;
361
-
362
- // fast-deep-equal version
363
- if (isEqual !== undefined) {
364
- bRet = !isEqual(this.oCaseInfo, oCurrentCaseInfo);
365
- } else {
366
- const sCurrnentCaseInfo = JSON.stringify(oCurrentCaseInfo);
367
- const sOldCaseInfo = JSON.stringify(this.oCaseInfo);
368
- // stringify compare version
369
- if (sCurrnentCaseInfo != sOldCaseInfo) {
370
- bRet = true;
371
- }
372
- }
317
+ // Open/update/close are derived purely from how the count of known modals compares to
318
+ // routingInfo.accessedOrder — mirrors react-sdk's modal-stack fix so that closing one modal
319
+ // (removing one entry from accessedOrder) can never be mistaken for closing all of them.
320
+ isOpenModalAction(accessedOrder: string[]): boolean {
321
+ return Object.keys(this.modalCollection).length < accessedOrder.length;
322
+ }
373
323
 
374
- // if different, save off new case info
375
- if (bRet) {
376
- this.oCaseInfo = JSON.parse(JSON.stringify(oCurrentCaseInfo));
377
- }
324
+ isUpdateModalAction(accessedOrder: string[]): boolean {
325
+ return Object.keys(this.modalCollection).length === accessedOrder.length;
326
+ }
327
+
328
+ isCloseModalAction(accessedOrder: string[]): boolean {
329
+ return Object.keys(this.modalCollection).length > accessedOrder.length;
330
+ }
331
+
332
+ handleModalOpen(key: string) {
333
+ this.modalCollection = { ...this.modalCollection, [key]: {} };
334
+ }
378
335
 
379
- return bRet;
336
+ handleModalClose(accessedOrder: string[]) {
337
+ const closedModalKey = Object.keys(this.modalCollection).find(modalKey => !accessedOrder.includes(modalKey));
338
+
339
+ if (closedModalKey) {
340
+ const updatedModalCollection = { ...this.modalCollection };
341
+ delete updatedModalCollection[closedModalKey];
342
+ this.modalCollection = updatedModalCollection;
343
+
344
+ this.modalStack = this.modalStack.filter(modal => modal.key !== closedModalKey);
345
+ this.modalVisibleChange.emit(this.modalStack.length > 0);
346
+ this.bannerService.clearBanners(closedModalKey);
347
+ this.cdRef.markForCheck();
348
+ }
380
349
  }
381
350
 
382
- getBanners() {
383
- return getBanners({ target: this.itemKey$, ...this.stateProps$ });
351
+ getModalHeading(dataObjectAction, actionName) {
352
+ switch (dataObjectAction) {
353
+ case PCore.getConstants().RESOURCE_STATUS.CREATE:
354
+ return this.localizedVal('Add Record', this.localeCategory);
355
+ case PCore.getConstants().RESOURCE_STATUS.OPEN_FLOW_ACTION:
356
+ return this.localizedVal(actionName, this.localeCategory);
357
+ default:
358
+ return this.localizedVal('Edit Record', this.localeCategory);
359
+ }
384
360
  }
385
361
 
386
- getModalHeading(dataObjectAction) {
387
- return dataObjectAction === PCore.getConstants().RESOURCE_STATUS.CREATE
388
- ? this.localizedVal('Add Record', this.localeCategory)
389
- : this.localizedVal('Edit Record', this.localeCategory);
362
+ getHeadingValue(latestItem, isMultiRecordData, isDataObject, actionName, dataObjectAction, caseTypeName, ID, caseLocaleRef) {
363
+ if (isMultiRecordData) {
364
+ return latestItem.heading;
365
+ }
366
+ if (isDataObject) {
367
+ if (actionName) {
368
+ return this.localizedVal(actionName, this.localeCategory);
369
+ }
370
+ return this.getModalHeading(dataObjectAction, actionName);
371
+ }
372
+ return this.determineModalHeaderByAction(actionName, caseTypeName, ID, caseLocaleRef);
390
373
  }
391
374
 
392
375
  determineModalHeaderByAction(actionName, caseTypeName, ID, caseLocaleRef) {
@@ -396,15 +379,23 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
396
379
  return `${this.localizedVal('Create', this.localeCategory)} ${this.localizedVal(caseTypeName, undefined, caseLocaleRef)} (${ID})`;
397
380
  }
398
381
 
399
- closeActionsDialog = () => {
382
+ // Closes one modal by key (bound per-instance in the template), or the topmost when omitted
383
+ closeActionsDialog = (modalKey?: string) => {
400
384
  this.actionsDialog = true;
401
- // this.ngZone.run(() => {
402
- this.bShowModal$ = false;
403
385
 
404
- // for when non modal
405
- this.modalVisibleChange.emit(this.bShowModal$);
386
+ this.modalStack = modalKey ? this.modalStack.filter(modal => modal.key !== modalKey) : this.modalStack.slice(0, -1);
406
387
 
407
- this.oCaseInfo = {};
408
- // });
388
+ this.modalVisibleChange.emit(this.modalStack.length > 0);
389
+ this.bannerService.clearBanners(modalKey ?? '');
390
+ this.cdRef.markForCheck();
409
391
  };
392
+
393
+ // Binds closeActionsDialog to a specific modal's key so closing one stacked modal never affects the others
394
+ closeModalFor(modalKey: string) {
395
+ return () => this.closeActionsDialog(modalKey);
396
+ }
397
+
398
+ trackByModalKey(index: number, modal: ModalEntry) {
399
+ return modal.key;
400
+ }
410
401
  }
@@ -1,4 +1,4 @@
1
- import { Component, OnInit, Input, NgZone, forwardRef, OnDestroy } from '@angular/core';
1
+ import { ChangeDetectorRef, Component, OnInit, Input, forwardRef, OnDestroy } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import { FormGroup } from '@angular/forms';
4
4
  import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
@@ -56,7 +56,7 @@ export class ViewContainerComponent implements OnInit, OnDestroy {
56
56
  constructor(
57
57
  private angularPConnect: AngularPConnectService,
58
58
  private psService: ProgressSpinnerService,
59
- private ngZone: NgZone
59
+ private cdRef: ChangeDetectorRef
60
60
  ) {}
61
61
 
62
62
  ngOnInit() {
@@ -166,21 +166,19 @@ export class ViewContainerComponent implements OnInit, OnDestroy {
166
166
  const { accessedOrder, items } = routingInfo;
167
167
  if (accessedOrder && items) {
168
168
  const key = accessedOrder[accessedOrder.length - 1];
169
- let componentVisible = accessedOrder.length > 0;
170
- const { visible } = this.state;
171
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
172
- componentVisible = visible || componentVisible;
173
169
  if (items[key] && items[key].view && Object.keys(items[key].view).length > 0) {
174
170
  const latestItem = items[key];
175
171
  const rootView = latestItem.view;
176
172
  const { context, name: viewName } = rootView.config;
173
+ const target = key.substring(0, key.lastIndexOf('_'));
177
174
  const config: any = { meta: rootView };
178
175
  config.options = {
179
176
  context: latestItem.context,
180
177
  pageReference: context || this.pConn$.getPageReference(),
181
178
  containerName: this.pConn$.getContainerName(),
182
179
  containerItemName: key,
183
- hasForm: viewName === CREATE_DETAILS_VIEW_NAME
180
+ hasForm: viewName === CREATE_DETAILS_VIEW_NAME,
181
+ target
184
182
  };
185
183
  const configObject = PCore.createPConnect(config);
186
184
 
@@ -201,23 +199,22 @@ export class ViewContainerComponent implements OnInit, OnDestroy {
201
199
  // ViewContainer HTML template to guide what's rendered similar to what
202
200
  // the Nebula/Constellation return of React.Fragment does
203
201
 
204
- this.ngZone.run(() => {
205
- if (newComp.getComponentName() === 'reference') {
206
- // if a refernece, it will de reference to a "view"
207
- // so hand this off to the "View" component to do that
208
- this.isViewContainer$ = false;
209
- this.viewPConn$ = newComp;
202
+ if (newComp.getComponentName() === 'reference') {
203
+ // if a refernece, it will de reference to a "view"
204
+ // so hand this off to the "View" component to do that
205
+ this.isViewContainer$ = false;
206
+ this.viewPConn$ = newComp;
210
207
 
211
- /*
212
- *** this commmented out code should be removed, once we test out that
213
- *** handing off refernce to View component always works
214
- */
208
+ /*
209
+ *** this commmented out code should be removed, once we test out that
210
+ *** handing off refernce to View component always works
211
+ */
215
212
 
216
- // When newComp is a reference, we want to de-reference
217
- // it (to get the View) and then use that View to get the
218
- // template, title, children, etc.
213
+ // When newComp is a reference, we want to de-reference
214
+ // it (to get the View) and then use that View to get the
215
+ // template, title, children, etc.
219
216
 
220
- /*
217
+ /*
221
218
 
222
219
  const theDereferencedView = newComp;
223
220
  const newConfigProps = theDereferencedView.getConfigProps();
@@ -230,20 +227,20 @@ export class ViewContainerComponent implements OnInit, OnDestroy {
230
227
  this.arChildren$ = theDereferencedViewChildren;
231
228
  this.createdViewPConn$ = theDereferencedView;
232
229
  */
233
- } else {
234
- // old style when newComp is NOT a 'reference'
235
- this.isViewContainer$ = true;
236
-
237
- console.error(`ViewContainer has a newComp that is NOT a reference!`);
238
-
239
- this.createdViewPConn$ = newComp;
240
- const newConfigProps = newComp.getConfigProps();
241
- this.templateName$ = newConfigProps.template || '';
242
- this.title$ = newConfigProps.title || '';
243
- // update children with new view's children
244
- this.arChildren$ = newComp.getChildren();
245
- }
246
- });
230
+ } else {
231
+ // old style when newComp is NOT a 'reference'
232
+ this.isViewContainer$ = true;
233
+
234
+ console.error(`ViewContainer has a newComp that is NOT a reference!`);
235
+
236
+ this.createdViewPConn$ = newComp;
237
+ const newConfigProps = newComp.getConfigProps();
238
+ this.templateName$ = newConfigProps.template || '';
239
+ this.title$ = newConfigProps.title || '';
240
+ // update children with new view's children
241
+ this.arChildren$ = newComp.getChildren();
242
+ }
243
+ this.cdRef.markForCheck();
247
244
  }
248
245
  }
249
246
  }
@@ -1,5 +1,5 @@
1
1
  <div>
2
- <div><component-mapper name="AlertBanner" [props]="{ banners: bannerService.banners }" [parent]="this"></component-mapper></div>
2
+ <div><component-mapper name="AlertBanner" [props]="{ banners: bannerService.getBanners(itemKey$) }" [parent]="this"></component-mapper></div>
3
3
  <div *ngIf="bHasNavigation$" class="psdk-stepper">
4
4
  <component-mapper
5
5
  name="MultiStep"
@@ -10,6 +10,7 @@
10
10
  arMainButtons$,
11
11
  arSecondaryButtons$,
12
12
  bIsVertical$,
13
+ stepIndicator$,
13
14
  arCurrentStepIndicies$,
14
15
  arNavigationSteps$
15
16
  }"