@sap-ux/preview-middleware 0.16.112 → 0.16.113

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 (41) hide show
  1. package/dist/client/adp/quick-actions/common/create-page-action.js +2 -3
  2. package/dist/client/adp/quick-actions/common/create-page-action.ts +1 -5
  3. package/dist/client/adp/quick-actions/fe-v2/change-table-columns.js +1 -8
  4. package/dist/client/adp/quick-actions/fe-v2/change-table-columns.ts +1 -10
  5. package/dist/client/adp/quick-actions/fe-v2/create-table-action.js +1 -8
  6. package/dist/client/adp/quick-actions/fe-v2/create-table-action.ts +0 -8
  7. package/dist/client/adp/quick-actions/fe-v2/create-table-custom-column.js +1 -12
  8. package/dist/client/adp/quick-actions/fe-v2/create-table-custom-column.ts +0 -11
  9. package/dist/client/adp/quick-actions/fe-v2/lr-enable-semantic-date-range-filter-bar.js +5 -1
  10. package/dist/client/adp/quick-actions/fe-v2/lr-enable-semantic-date-range-filter-bar.ts +5 -1
  11. package/dist/client/adp/quick-actions/fe-v2/registry.js +2 -2
  12. package/dist/client/adp/quick-actions/fe-v2/registry.ts +6 -5
  13. package/dist/client/adp/quick-actions/fe-v4/create-table-custom-column.js +1 -12
  14. package/dist/client/adp/quick-actions/fe-v4/create-table-custom-column.ts +0 -11
  15. package/dist/client/adp/quick-actions/fe-v4/registry.js +2 -2
  16. package/dist/client/adp/quick-actions/fe-v4/registry.ts +6 -6
  17. package/dist/client/adp/quick-actions/fe-v4/utils.js +4 -44
  18. package/dist/client/adp/quick-actions/fe-v4/utils.ts +2 -47
  19. package/dist/client/cpe/changes/flex-change.js +60 -51
  20. package/dist/client/cpe/changes/flex-change.ts +59 -26
  21. package/dist/client/cpe/changes/service.js +150 -34
  22. package/dist/client/cpe/changes/service.ts +239 -54
  23. package/dist/client/cpe/control-data.js +104 -28
  24. package/dist/client/cpe/control-data.ts +134 -43
  25. package/dist/client/cpe/init.js +3 -3
  26. package/dist/client/cpe/init.ts +5 -8
  27. package/dist/client/cpe/outline/editable.js +3 -3
  28. package/dist/client/cpe/outline/editable.ts +3 -2
  29. package/dist/client/cpe/outline/nodes.js +39 -9
  30. package/dist/client/cpe/outline/nodes.ts +61 -10
  31. package/dist/client/cpe/outline/service.js +3 -2
  32. package/dist/client/cpe/outline/service.ts +11 -3
  33. package/dist/client/cpe/selection.js +29 -16
  34. package/dist/client/cpe/selection.ts +38 -20
  35. package/dist/client/cpe/utils.js +42 -1
  36. package/dist/client/cpe/utils.ts +74 -0
  37. package/dist/client/thirdparty/@sap-ux-private/control-property-editor-common.js +7 -1
  38. package/dist/client/tsconfig.tsbuildinfo +1 -1
  39. package/dist/client/utils/fe-v4.js +87 -0
  40. package/dist/client/utils/fe-v4.ts +81 -0
  41. package/package.json +5 -5
@@ -3,7 +3,10 @@ import type {
3
3
  PendingChange,
4
4
  SavedPropertyChange,
5
5
  UnknownSavedChange,
6
- SavedControlChange
6
+ SavedControlChange,
7
+ PendingConfigurationChange,
8
+ SavedConfigurationChange,
9
+ PropertyValue
7
10
  } from '@sap-ux-private/control-property-editor-common';
8
11
  import {
9
12
  changeProperty,
@@ -13,10 +16,11 @@ import {
13
16
  FlexChangesEndPoints,
14
17
  reloadApplication,
15
18
  setApplicationRequiresReload,
16
- save
19
+ save,
20
+ CONFIGURATION_CHANGE_KIND,
21
+ PropertyType
17
22
  } from '@sap-ux-private/control-property-editor-common';
18
23
  import { applyChange } from './flex-change';
19
- import type { SelectionService } from '../selection';
20
24
  import type { ActionSenderFunction, SubscribeFunction, UI5AdaptationOptions } from '../types';
21
25
  import type Event from 'sap/ui/base/Event';
22
26
  import type FlexCommand from 'sap/ui/rta/command/FlexCommand';
@@ -28,31 +32,59 @@ import { getError } from '../../utils/error';
28
32
  import { isLowerThanMinimalUi5Version, getUi5Version } from '../../utils/version';
29
33
  import MessageToast from 'sap/m/MessageToast';
30
34
  import { getTextBundle } from '../../i18n';
35
+ import { getControlById, isA } from '../../utils/core';
36
+ import UI5Element from 'sap/ui/core/Element';
37
+ import { getConfigMapControlIdMap } from '../../utils/fe-v4';
31
38
 
32
39
  interface ChangeContent {
33
40
  property: string;
34
41
  newValue: string;
35
42
  newBinding: string;
36
43
  }
44
+ export const STACK_CHANGE_EVENT = 'STACK_CHANGED';
45
+ export interface StackChangedEventDetail {
46
+ controls: UI5Element[];
47
+ }
48
+
49
+ interface ConfigurationChangeContent {
50
+ page: string;
51
+ entityPropertyChange: {
52
+ propertyPath: string;
53
+ operation: 'UPSERT' | 'DELETE' | 'INSERT' | 'UPDATE';
54
+ propertyValue: string;
55
+ };
56
+ }
37
57
 
38
58
  interface ChangeSelector {
39
59
  id: string;
40
60
  type: string;
41
61
  }
42
62
 
43
- interface Change {
63
+ interface BaseChange {
44
64
  fileName: string;
45
- controlId: string;
46
- propertyName: string;
47
- value: string;
48
65
  timestamp: string;
66
+ creation: string;
67
+ value: string;
49
68
  selector: ChangeSelector;
69
+ }
70
+
71
+ const PROPRTY_CHANGE = 'propertyChange';
72
+ const PROPRTY_BINDING_CHANGE = 'propertyBindingChange';
73
+ const MANIFEST_V4_CHANGE = 'appdescr_fe_changePageConfiguration';
74
+
75
+ interface PropertyChange extends BaseChange {
76
+ changeType: typeof PROPRTY_CHANGE | typeof PROPRTY_BINDING_CHANGE;
77
+ controlId: string;
78
+ propertyName: string;
50
79
  content: ChangeContent;
51
- creation: string;
52
- changeType: string;
80
+ }
81
+ interface ConfigChange extends BaseChange {
82
+ changeType: typeof MANIFEST_V4_CHANGE;
83
+ propertyName: string;
84
+ content: ConfigurationChangeContent;
53
85
  }
54
86
 
55
- type SavedChangesResponse = Record<string, Change>;
87
+ type SavedChangesResponse = Record<string, PropertyChange | ConfigChange>;
56
88
 
57
89
  type Properties<T extends object> = { [K in keyof T]-?: K extends string ? K : never }[keyof T];
58
90
  /**
@@ -75,12 +107,23 @@ function assertProperties<T extends object>(properties: Properties<T>[], target:
75
107
  *
76
108
  * @param change Change object
77
109
  */
78
- function assertChange(change: Change): void {
110
+ function assertChange(change: PropertyChange): void {
79
111
  assertProperties(['fileName', 'selector', 'content', 'creation'], change);
80
112
  assertProperties(['id'], change.selector);
81
113
  assertProperties(['property'], change.content);
82
114
  }
83
115
 
116
+ /**
117
+ * Assert v4 manifest change for its validity. Throws error if no value found in saved changes.
118
+ *
119
+ * @param change Change object
120
+ */
121
+ function assertManifestChange(change: ConfigChange): void {
122
+ assertProperties(['fileName', 'content', 'creation'], change);
123
+ assertProperties(['page', 'entityPropertyChange'], change.content);
124
+ assertProperties(['propertyPath', 'operation', 'propertyValue'], change.content.entityPropertyChange);
125
+ }
126
+
84
127
  /**
85
128
  * Modify rta message.
86
129
  *
@@ -93,23 +136,44 @@ function modifyRTAErrorMessage(errorMessage: string, id: string, type: string):
93
136
  return errorMessage.replace('Error: Applying property changes failed:', '').replace(`${type}#${id}`, '');
94
137
  }
95
138
 
139
+ /**
140
+ * Returns a shortened version of the given configuration path segments by removing excess segments,
141
+ * leaving only the most relevant parts for display. For example, the configuration path
142
+ * `controlConfiguration/com.sap.UI.v1.LineItem/tableSettings` will be shortened to
143
+ * `LineItem/tableSettings`.
144
+ *
145
+ * @param propertyPathSeg string[]
146
+ * @returns string
147
+ */
148
+ function getCompactV4ConfigPath(propertyPathSeg: string[]): string {
149
+ return propertyPathSeg.join('/').replace(/^controlConfiguration\/(?:([^/]+\/))?@[^/]+\.v1\./, '$1');
150
+ }
151
+
96
152
  /**
97
153
  * A Class of ChangeService
98
154
  */
99
- export class ChangeService {
100
- private savedChanges: SavedPropertyChange[] | UnknownSavedChange[] | SavedControlChange[] = [];
155
+ export class ChangeService extends EventTarget {
156
+ private savedChanges:
157
+ | SavedPropertyChange[]
158
+ | UnknownSavedChange[]
159
+ | SavedControlChange[]
160
+ | SavedConfigurationChange[] = [];
101
161
  private changesRequiringReload = 0;
102
162
  private sendAction: (action: ExternalAction) => void;
103
163
  private pendingChanges: PendingChange[] = [];
104
164
  private changedFiles: Record<string, object> = {};
105
165
  private readonly eventStack: object[] = [];
166
+ private pendingConfigChangeMap: Map<string, PendingConfigurationChange[]> = new Map();
167
+ private configPropertyControlIdMap: Map<string, string[]> = new Map();
106
168
  /**
107
169
  *
108
170
  * @param options ui5 adaptation options.
109
171
  * @param ui5 facade for ui5 framework methods.
110
172
  * @param selectionService selection service instance.
111
173
  */
112
- constructor(private readonly options: UI5AdaptationOptions, private readonly selectionService: SelectionService) {}
174
+ constructor(private readonly options: UI5AdaptationOptions) {
175
+ super();
176
+ }
113
177
 
114
178
  /**
115
179
  * Initializes change service.
@@ -122,10 +186,6 @@ export class ChangeService {
122
186
  subscribe(async (action): Promise<void> => {
123
187
  if (changeProperty.match(action)) {
124
188
  try {
125
- this.selectionService.applyControlPropertyChange(
126
- action.payload.controlId,
127
- action.payload.propertyName
128
- );
129
189
  await applyChange(this.options, action.payload);
130
190
  } catch (exception) {
131
191
  // send error information
@@ -174,6 +234,27 @@ export class ChangeService {
174
234
  );
175
235
  }
176
236
 
237
+ private getSavedConfigurationChange(change: ConfigChange): SavedConfigurationChange {
238
+ assertManifestChange(change);
239
+ if ([change.content.entityPropertyChange.propertyValue].every((item) => item === undefined || item === null)) {
240
+ throw new Error('Invalid change, missing property value on change file');
241
+ }
242
+ const propertyPathSegments = change.content.entityPropertyChange.propertyPath.split('/');
243
+ const propertyName = propertyPathSegments.pop();
244
+ const configMapKey = getConfigMapControlIdMap(change.content.page, propertyPathSegments);
245
+ const controlIds = this.configPropertyControlIdMap?.get(configMapKey) || [];
246
+ return {
247
+ type: 'saved',
248
+ kind: 'configuration',
249
+ fileName: change.fileName,
250
+ controlIds,
251
+ propertyPath: getCompactV4ConfigPath(propertyPathSegments) || change.content.page,
252
+ propertyName: propertyName ?? '',
253
+ value: change.content.entityPropertyChange.propertyValue,
254
+ timestamp: new Date(change.creation).getTime()
255
+ };
256
+ }
257
+
177
258
  /**
178
259
  * Fetches saved changes from the workspace and sorts them.
179
260
  */
@@ -187,40 +268,50 @@ export class ChangeService {
187
268
  Object.keys(savedChanges ?? {}).map(
188
269
  async (
189
270
  key
190
- ): Promise<SavedPropertyChange | UnknownSavedChange | SavedControlChange | undefined> => {
191
- const change: Change = savedChanges[key];
271
+ ): Promise<
272
+ | SavedPropertyChange
273
+ | UnknownSavedChange
274
+ | SavedControlChange
275
+ | SavedConfigurationChange
276
+ | undefined
277
+ > => {
278
+ const change: PropertyChange | ConfigChange = savedChanges[key];
192
279
  let selectorId;
193
280
  try {
194
- const flexObject = await this.getFlexObject(change);
195
- selectorId = await this.getControlIdByChange(flexObject);
196
- assertChange(change);
197
- if (
198
- [change.content.newValue, change.content.newBinding].every(
199
- (item) => item === undefined || item === null
200
- )
201
- ) {
202
- throw new Error('Invalid change, missing new value in the change file');
203
- }
204
- if (
205
- change.changeType !== 'propertyChange' &&
206
- change.changeType !== 'propertyBindingChange'
207
- ) {
208
- throw new Error('Unknown Change Type');
281
+ if (change.changeType === MANIFEST_V4_CHANGE) {
282
+ return this.getSavedConfigurationChange(change);
283
+ } else {
284
+ const flexObject = await this.getFlexObject(change);
285
+ selectorId = await this.getControlIdByChange(flexObject);
286
+ assertChange(change);
287
+ if (
288
+ [change.content.newValue, change.content.newBinding].every(
289
+ (item) => item === undefined || item === null
290
+ )
291
+ ) {
292
+ throw new Error('Invalid change, missing new value in the change file');
293
+ }
294
+ if (
295
+ change.changeType !== 'propertyChange' &&
296
+ change.changeType !== 'propertyBindingChange'
297
+ ) {
298
+ throw new Error('Unknown Change Type');
299
+ }
300
+ this.changedFiles[change.fileName] = change;
301
+ return {
302
+ type: 'saved',
303
+ kind: 'property',
304
+ fileName: change.fileName,
305
+ controlId: selectorId,
306
+ propertyName: change.content.property,
307
+ value: change.content.newValue ?? change.content.newBinding,
308
+ timestamp: new Date(change.creation).getTime(),
309
+ controlName: change.selector.type
310
+ ? (change.selector.type.split('.').pop() as string)
311
+ : '',
312
+ changeType: change.changeType
313
+ } as SavedPropertyChange;
209
314
  }
210
- this.changedFiles[change.fileName] = change;
211
- return {
212
- type: 'saved',
213
- kind: 'property',
214
- fileName: change.fileName,
215
- controlId: selectorId,
216
- propertyName: change.content.property,
217
- value: change.content.newValue ?? change.content.newBinding,
218
- timestamp: new Date(change.creation).getTime(),
219
- controlName: change.selector.type
220
- ? (change.selector.type.split('.').pop() as string)
221
- : '',
222
- changeType: change.changeType
223
- } as SavedPropertyChange;
224
315
  } catch (error) {
225
316
  // Gracefully handle change files with invalid content
226
317
  if (change.fileName) {
@@ -276,6 +367,7 @@ export class ChangeService {
276
367
  if (change.kind === 'control') {
277
368
  return change.controlId === controlId;
278
369
  }
370
+ return false;
279
371
  })
280
372
  .map((change) =>
281
373
  fetch(FlexChangesEndPoints.changes, {
@@ -311,6 +403,7 @@ export class ChangeService {
311
403
  typeof command.getCommands === 'function' ? command.getCommands() : [command]
312
404
  );
313
405
  const activeCommandCount = allCommandsFlattened.length - executedCommands.length;
406
+ this.pendingConfigChangeMap = new Map();
314
407
  let i = 0;
315
408
  for (const command of allCommands) {
316
409
  try {
@@ -334,10 +427,8 @@ export class ChangeService {
334
427
  this.pendingChanges = pendingChanges.filter((change): boolean => !!change);
335
428
  const changesRequiringReload = this.pendingChanges.reduce(
336
429
  (sum, change) =>
337
- [
338
- 'appdescr_fe_changePageConfiguration',
339
- 'appdescr_ui_generic_app_changePageConfiguration'
340
- ].includes(change.changeType)
430
+ change.kind === CONFIGURATION_CHANGE_KIND ||
431
+ change.changeType === 'appdescr_ui_generic_app_changePageConfiguration'
341
432
  ? sum + 1
342
433
  : sum,
343
434
  0
@@ -353,12 +444,54 @@ export class ChangeService {
353
444
  this.eventStack.splice(eventIndex, 1);
354
445
  if (Array.isArray(allCommands) && allCommands.length === 0) {
355
446
  this.pendingChanges = [];
447
+ this.pendingConfigChangeMap = new Map();
356
448
  await this.fetchSavedChanges();
357
449
  }
450
+
451
+ // Notify to update the ui for configuration changes.
452
+ const configurationChanges = this.pendingChanges?.filter((item) => item.kind === 'configuration');
453
+ if (configurationChanges.length) {
454
+ const stackChangeEvent = new CustomEvent(STACK_CHANGE_EVENT, {
455
+ detail: {
456
+ controls: configurationChanges.reduce((acc: UI5Element[], item) => {
457
+ const controls = (item.controlIds || [])
458
+ .map((id: string) => {
459
+ return getControlById(id);
460
+ })
461
+ .filter((ui5Element) => isA<UI5Element>('sap.ui.core.Element', ui5Element));
462
+ acc.push(...controls);
463
+ return acc;
464
+ }, [])
465
+ }
466
+ });
467
+ this.dispatchEvent(stackChangeEvent);
468
+ }
358
469
  this.updateStack();
359
470
  handleStackChange();
360
471
  };
361
472
  }
473
+ /**
474
+ * Cached configuration commands to set reset value during stack change event.
475
+ *
476
+ * @param {string} controlId - control id of the config property.
477
+ * @param {string} propertyName - config property name.
478
+ * @returns {PropertyValue | undefined}.
479
+ */
480
+ public getConfigurationPropertyValue(controlId: string, propertyName: string): PropertyValue | undefined {
481
+ const pendingChanges = this.pendingConfigChangeMap?.get(controlId);
482
+ return (pendingChanges || []).find((item) => item.isActive && item.propertyName === propertyName)?.value;
483
+ }
484
+
485
+ /**
486
+ * Update config changes with assocaited controls.
487
+ *
488
+ * @param {Map<string, string[]>} configPropertyControlIdMap - config property path control id map.
489
+ */
490
+ public async updateConfigurationProps(configPropertyControlIdMap: Map<string, string[]>): Promise<void> {
491
+ this.configPropertyControlIdMap = configPropertyControlIdMap;
492
+ await this.fetchSavedChanges();
493
+ this.updateStack();
494
+ }
362
495
 
363
496
  /**
364
497
  * Handles a command by preparing a pending change and adding it to the list of pending changes.
@@ -381,6 +514,47 @@ export class ChangeService {
381
514
  }
382
515
  }
383
516
 
517
+ private preparev4ConfigurationChange(
518
+ command: FlexCommand,
519
+ value: string,
520
+ fileName: string,
521
+ index: number,
522
+ inactiveCommandCount: number
523
+ ): PendingConfigurationChange {
524
+ const { entityPropertyChange, page } = command.getProperty('parameters') as {
525
+ entityPropertyChange: {
526
+ propertyPath: string;
527
+ };
528
+ page: string;
529
+ };
530
+ const controlId = this.getCommandSelectorId(command) ?? '';
531
+ const propertyPathSegments = entityPropertyChange.propertyPath.split('/');
532
+ const propName = propertyPathSegments.pop();
533
+ const key = getConfigMapControlIdMap(page, propertyPathSegments);
534
+
535
+ const isActive = index >= inactiveCommandCount;
536
+ const controlIds = this.configPropertyControlIdMap?.get(key) || [controlId];
537
+ const result: PendingConfigurationChange = {
538
+ type: 'pending',
539
+ kind: 'configuration',
540
+ controlIds,
541
+ propertyPath: getCompactV4ConfigPath(propertyPathSegments) || page,
542
+ propertyName: propName as string,
543
+ isActive,
544
+ value,
545
+ fileName
546
+ };
547
+ for (const id of result.controlIds) {
548
+ if (!this.pendingConfigChangeMap.get(id)) {
549
+ this.pendingConfigChangeMap.set(id, []);
550
+ }
551
+ const pendingChanges = this.pendingConfigChangeMap.get(id);
552
+ pendingChanges?.push(result);
553
+ }
554
+
555
+ return result;
556
+ }
557
+
384
558
  /**
385
559
  * Prepares the type of change based on the command and other parameters.
386
560
  *
@@ -417,6 +591,10 @@ export class ChangeService {
417
591
  case 'propertyBindingChange':
418
592
  value = command.getProperty('newBinding') as string;
419
593
  break;
594
+ case 'appdescr_fe_changePageConfiguration':
595
+ value = (command.getProperty('parameters') as { entityPropertyChange: { propertyValue: unknown } })
596
+ .entityPropertyChange.propertyValue as string;
597
+ break;
420
598
  }
421
599
 
422
600
  const { fileName } = change.getDefinition();
@@ -426,12 +604,15 @@ export class ChangeService {
426
604
  kind: 'property',
427
605
  changeType,
428
606
  controlId: selectorId,
607
+ propertyType: PropertyType.ControlProperty,
429
608
  propertyName: command.getProperty('propertyName') as string,
430
609
  isActive: index >= inactiveCommandCount,
431
610
  value,
432
611
  controlName: command.getElement().getMetadata().getName().split('.').pop() ?? '',
433
612
  fileName
434
613
  };
614
+ } else if (changeType === 'appdescr_fe_changePageConfiguration') {
615
+ result = this.preparev4ConfigurationChange(command, value, fileName, index, inactiveCommandCount);
435
616
  } else {
436
617
  result = {
437
618
  type: 'pending',
@@ -564,7 +745,7 @@ export class ChangeService {
564
745
  */
565
746
  public async syncOutlineChanges(): Promise<void> {
566
747
  for (const change of this.savedChanges) {
567
- if (change.kind !== 'unknown') {
748
+ if (change.kind !== 'unknown' && change.kind !== 'configuration') {
568
749
  const flexObject = await this.getFlexObject(this.changedFiles[change.fileName]);
569
750
  change.controlId = (await this.getControlIdByChange(flexObject)) ?? '';
570
751
  }
@@ -572,6 +753,10 @@ export class ChangeService {
572
753
  this.updateStack();
573
754
  }
574
755
 
756
+ public onStackChange(handler: (event: CustomEvent<StackChangedEventDetail>) => void | Promise<void>): void {
757
+ this.addEventListener(STACK_CHANGE_EVENT, handler as EventListener);
758
+ }
759
+
575
760
  /**
576
761
  * Get FlexObject from change object based on UI5 version.
577
762
  *