@sap-ux/preview-middleware 0.16.91 → 0.16.93

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.
@@ -4,11 +4,26 @@ sap.ui.define([
4
4
  './flex-change',
5
5
  'sap/base/Log',
6
6
  '../rta-service',
7
+ 'sap/ui/core/util/reflection/JsControlTreeModifier',
7
8
  '../../utils/error',
9
+ '../../utils/version',
8
10
  'sap/m/MessageToast',
9
11
  '../../i18n'
10
- ], function (___sap_ux_private_control_property_editor_common, ___flex_change, Log, ___rta_service, ____utils_error, MessageToast, ____i18n) {
12
+ ], function (___sap_ux_private_control_property_editor_common, ___flex_change, Log, ___rta_service, JsControlTreeModifier, ____utils_error, ____utils_version, MessageToast, ____i18n) {
11
13
  'use strict';
14
+ function __ui5_require_async(path) {
15
+ return new Promise(function (resolve, reject) {
16
+ sap.ui.require([path], function (module) {
17
+ if (!(module && module.__esModule)) {
18
+ module = module === null || !(typeof module === 'object' && path.endsWith('/library')) ? { default: module } : module;
19
+ Object.defineProperty(module, '__esModule', { value: true });
20
+ }
21
+ resolve(module);
22
+ }, function (err) {
23
+ reject(err);
24
+ });
25
+ });
26
+ }
12
27
  const changeProperty = ___sap_ux_private_control_property_editor_common['changeProperty'];
13
28
  const changeStackModified = ___sap_ux_private_control_property_editor_common['changeStackModified'];
14
29
  const deletePropertyChanges = ___sap_ux_private_control_property_editor_common['deletePropertyChanges'];
@@ -20,6 +35,8 @@ sap.ui.define([
20
35
  const applyChange = ___flex_change['applyChange'];
21
36
  const modeAndStackChangeHandler = ___rta_service['modeAndStackChangeHandler'];
22
37
  const getError = ____utils_error['getError'];
38
+ const isLowerThanMinimalUi5Version = ____utils_version['isLowerThanMinimalUi5Version'];
39
+ const getUi5Version = ____utils_version['getUi5Version'];
23
40
  const getTextBundle = ____i18n['getTextBundle'];
24
41
  function assertProperties(properties, target) {
25
42
  for (const property of properties) {
@@ -45,6 +62,9 @@ sap.ui.define([
45
62
  class ChangeService {
46
63
  savedChanges = [];
47
64
  changesRequiringReload = 0;
65
+ pendingChanges = [];
66
+ changedFiles = {};
67
+ eventStack = [];
48
68
  constructor(options, selectionService) {
49
69
  this.options = options;
50
70
  this.selectionService = selectionService;
@@ -86,18 +106,21 @@ sap.ui.define([
86
106
  this.options.rta.attachUndoRedoStackModified(this.createOnStackChangeHandler());
87
107
  }
88
108
  updateStack() {
89
- let pendingChanges = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
90
109
  this.sendAction(changeStackModified({
91
- saved: this.savedChanges,
92
- pending: pendingChanges
110
+ saved: this.savedChanges ?? [],
111
+ pending: this.pendingChanges ?? []
93
112
  }));
94
113
  }
95
114
  async fetchSavedChanges() {
115
+ this.changedFiles = {};
96
116
  const savedChangesResponse = await fetch(FlexChangesEndPoints.changes + `?_=${ Date.now() }`);
97
117
  const savedChanges = await savedChangesResponse.json();
98
- const changes = Object.keys(savedChanges ?? {}).map(key => {
118
+ const changes = (await Promise.all(Object.keys(savedChanges ?? {}).map(async key => {
99
119
  const change = savedChanges[key];
120
+ let selectorId;
100
121
  try {
122
+ const flexObject = await this.getFlexObject(change);
123
+ selectorId = await this.getControlIdByChange(flexObject);
101
124
  assertChange(change);
102
125
  if ([
103
126
  change.content.newValue,
@@ -108,11 +131,12 @@ sap.ui.define([
108
131
  if (change.changeType !== 'propertyChange' && change.changeType !== 'propertyBindingChange') {
109
132
  throw new Error('Unknown Change Type');
110
133
  }
134
+ this.changedFiles[change.fileName] = change;
111
135
  return {
112
136
  type: 'saved',
113
137
  kind: 'property',
114
138
  fileName: change.fileName,
115
- controlId: change.selector.id,
139
+ controlId: selectorId,
116
140
  propertyName: change.content.property,
117
141
  value: change.content.newValue ?? change.content.newBinding,
118
142
  timestamp: new Date(change.creation).getTime(),
@@ -121,25 +145,44 @@ sap.ui.define([
121
145
  };
122
146
  } catch (error) {
123
147
  if (change.fileName) {
148
+ this.changedFiles[change.fileName] = change;
124
149
  const unknownChange = {
125
150
  type: 'saved',
126
151
  kind: 'unknown',
127
152
  changeType: change.changeType,
128
153
  fileName: change.fileName,
129
- controlId: change.selector?.id
154
+ timestamp: new Date(change.creation).getTime()
130
155
  };
131
156
  if (change.creation) {
132
157
  unknownChange.timestamp = new Date(change.creation).getTime();
133
158
  }
159
+ if (selectorId) {
160
+ const controlChange = {
161
+ ...unknownChange,
162
+ kind: 'control',
163
+ controlId: selectorId
164
+ };
165
+ return controlChange;
166
+ }
134
167
  return unknownChange;
135
168
  }
136
169
  return undefined;
137
170
  }
138
- }).filter(change => !!change).sort((a, b) => b.timestamp - a.timestamp);
171
+ }))).filter(change => !!change).sort((a, b) => b.timestamp - a.timestamp);
139
172
  this.savedChanges = changes;
140
173
  }
141
174
  async deleteChange(controlId, propertyName, fileName) {
142
- const filesToDelete = this.savedChanges.filter(change => fileName ? fileName === change.fileName : change.controlId === controlId && change.propertyName === propertyName).map(change => fetch(FlexChangesEndPoints.changes, {
175
+ const filesToDelete = this.savedChanges.filter(change => {
176
+ if (fileName) {
177
+ return fileName === change.fileName;
178
+ }
179
+ if (change.kind === 'property') {
180
+ return change.controlId === controlId && change.propertyName === propertyName;
181
+ }
182
+ if (change.kind === 'control') {
183
+ return change.controlId === controlId;
184
+ }
185
+ }).map(change => fetch(FlexChangesEndPoints.changes, {
143
186
  method: 'DELETE',
144
187
  headers: { 'Content-Type': 'application/json' },
145
188
  body: JSON.stringify({ fileName: change.fileName })
@@ -150,53 +193,61 @@ sap.ui.define([
150
193
  }
151
194
  createOnStackChangeHandler() {
152
195
  const handleStackChange = modeAndStackChangeHandler(this.sendAction, this.options.rta);
153
- return async () => {
196
+ return async event => {
197
+ const pendingChanges = [];
198
+ this.eventStack.push(event);
154
199
  const stack = this.options.rta.getCommandStack();
155
200
  const allCommands = stack.getCommands();
156
201
  const executedCommands = stack.getAllExecutedCommands();
157
202
  const inactiveCommandCount = allCommands.length - executedCommands.length;
158
- let activeChanges = [];
159
- allCommands.forEach((command, i) => {
203
+ let i, command;
204
+ for ([i, command] of allCommands.entries()) {
160
205
  try {
161
206
  if (typeof command.getCommands === 'function') {
162
207
  const subCommands = command.getCommands();
163
- subCommands.forEach(subCommand => {
164
- const pendingChange = this.prepareChangeType(subCommand, inactiveCommandCount, i);
165
- if (pendingChange) {
166
- activeChanges.push(pendingChange);
167
- }
168
- });
169
- } else {
170
- const pendingChange = this.prepareChangeType(command, inactiveCommandCount, i);
171
- if (pendingChange) {
172
- activeChanges.push(pendingChange);
208
+ for (const subCommand of subCommands) {
209
+ await this.handleCommand(subCommand, inactiveCommandCount, i, pendingChanges);
173
210
  }
211
+ } else {
212
+ await this.handleCommand(command, inactiveCommandCount, i, pendingChanges);
174
213
  }
175
214
  } catch (error) {
176
215
  Log.error('CPE: Change creation Failed', getError(error));
177
216
  }
178
- });
179
- activeChanges = activeChanges.filter(change => !!change);
180
- const changesRequiringReload = activeChanges.reduce((sum, change) => change.changeType === 'appdescr_fe_changePageConfiguration' ? sum + 1 : sum, 0);
181
- if (changesRequiringReload > this.changesRequiringReload) {
182
- const resourceBundle = await getTextBundle();
183
- MessageToast.show(resourceBundle.getText('CPE_CHANGES_VISIBLE_AFTER_SAVE_AND_RELOAD_MESSAGE'), { duration: 8000 });
184
- this.sendAction(setApplicationRequiresReload(changesRequiringReload > 0));
185
217
  }
186
- this.changesRequiringReload = changesRequiringReload;
218
+ const resourceBundle = await getTextBundle();
219
+ const eventIndex = this.eventStack.indexOf(event);
220
+ if (this.eventStack.length - 1 === eventIndex) {
221
+ this.pendingChanges = pendingChanges.filter(change => !!change);
222
+ const changesRequiringReload = this.pendingChanges.reduce((sum, change) => change.changeType === 'appdescr_fe_changePageConfiguration' ? sum + 1 : sum, 0);
223
+ if (changesRequiringReload > this.changesRequiringReload) {
224
+ MessageToast.show(resourceBundle.getText('CPE_CHANGES_VISIBLE_AFTER_SAVE_AND_RELOAD_MESSAGE'), { duration: 8000 });
225
+ this.sendAction(setApplicationRequiresReload(changesRequiringReload > 0));
226
+ }
227
+ this.changesRequiringReload = changesRequiringReload;
228
+ }
229
+ this.eventStack.splice(eventIndex, 1);
187
230
  if (Array.isArray(allCommands) && allCommands.length === 0) {
231
+ this.pendingChanges = [];
188
232
  await this.fetchSavedChanges();
189
233
  }
190
- this.updateStack(activeChanges);
234
+ this.updateStack();
191
235
  handleStackChange();
192
236
  };
193
237
  }
194
- prepareChangeType(command, inactiveCommandCount, index) {
238
+ async handleCommand(command, inactiveCommandCount, index, pendingChanges) {
239
+ const pendingChange = await this.prepareChangeType(command, inactiveCommandCount, index);
240
+ if (pendingChange) {
241
+ pendingChanges.push(pendingChange);
242
+ }
243
+ }
244
+ async prepareChangeType(command, inactiveCommandCount, index) {
195
245
  let result;
196
246
  let value = '';
197
- const selectorId = this.getCommandSelectorId(command);
247
+ const change = command.getPreparedChange();
248
+ const selectorId = typeof change.getSelector === 'function' ? await this.getControlIdByChange(change) : this.getCommandSelectorId(command);
198
249
  const changeType = this.getCommandChangeType(command);
199
- if (!selectorId || !changeType) {
250
+ if (!changeType) {
200
251
  return undefined;
201
252
  }
202
253
  switch (changeType) {
@@ -207,8 +258,8 @@ sap.ui.define([
207
258
  value = command.getProperty('newBinding');
208
259
  break;
209
260
  }
210
- const {fileName} = command.getPreparedChange().getDefinition();
211
- if (changeType === 'propertyChange' || changeType === 'propertyBindingChange') {
261
+ const {fileName} = change.getDefinition();
262
+ if ((changeType === 'propertyChange' || changeType === 'propertyBindingChange') && selectorId) {
212
263
  result = {
213
264
  type: 'pending',
214
265
  kind: 'property',
@@ -224,12 +275,17 @@ sap.ui.define([
224
275
  result = {
225
276
  type: 'pending',
226
277
  kind: 'unknown',
227
- controlId: selectorId,
228
278
  changeType,
229
279
  isActive: index >= inactiveCommandCount,
230
- controlName: changeType === 'addXMLAtExtensionPoint' ? command.getSelector().name ?? '' : command.getElement().getMetadata().getName().split('.').pop() ?? '',
231
280
  fileName
232
281
  };
282
+ if (selectorId) {
283
+ result = {
284
+ ...result,
285
+ kind: 'control',
286
+ controlId: selectorId
287
+ };
288
+ }
233
289
  }
234
290
  return result;
235
291
  }
@@ -262,6 +318,56 @@ sap.ui.define([
262
318
  () => command.getParent()?.getElement().getId()
263
319
  ]);
264
320
  }
321
+ async getControlIdByChange(change) {
322
+ const appComponent = this.options.rta.getRootControlInstance();
323
+ const selector = typeof change.getSelector === 'function' ? change.getSelector() : undefined;
324
+ const changeType = change.getChangeType();
325
+ const layer = change.getLayer();
326
+ if (!selector?.id) {
327
+ return;
328
+ }
329
+ try {
330
+ let control = JsControlTreeModifier.bySelector(selector, appComponent);
331
+ if (!control) {
332
+ return selector.id;
333
+ }
334
+ const changeHandlerAPI = (await __ui5_require_async('sap/ui/fl/write/api/ChangesWriteAPI')).default;
335
+ const changeHandler = await changeHandlerAPI.getChangeHandler({
336
+ changeType,
337
+ element: control,
338
+ modifier: JsControlTreeModifier,
339
+ layer
340
+ });
341
+ if (changeHandler && typeof changeHandler.getChangeVisualizationInfo === 'function') {
342
+ const result = await changeHandler.getChangeVisualizationInfo(change, appComponent);
343
+ return JsControlTreeModifier.getControlIdBySelector(result?.affectedControls?.[0] ?? selector, appComponent);
344
+ }
345
+ return JsControlTreeModifier.getControlIdBySelector(selector, appComponent);
346
+ } catch (error) {
347
+ Log.error('Getting element ID from change has failed:', getError(error));
348
+ return selector.id;
349
+ }
350
+ }
351
+ async syncOutlineChanges() {
352
+ for (const change of this.savedChanges) {
353
+ if (change.kind !== 'unknown') {
354
+ const flexObject = await this.getFlexObject(this.changedFiles[change.fileName]);
355
+ change.controlId = await this.getControlIdByChange(flexObject) ?? '';
356
+ }
357
+ }
358
+ this.updateStack();
359
+ }
360
+ async getFlexObject(change) {
361
+ if (isLowerThanMinimalUi5Version(await getUi5Version(), {
362
+ major: 1,
363
+ minor: 109
364
+ })) {
365
+ const Change = (await __ui5_require_async('sap/ui/fl/Change')).default;
366
+ return new Change(change);
367
+ }
368
+ const FlexObjectFactory = (await __ui5_require_async('sap/ui/fl/apply/_internal/flexObjects/FlexObjectFactory')).default;
369
+ return FlexObjectFactory.createFromFileContent(change);
370
+ }
265
371
  }
266
372
  var __exports = { __esModule: true };
267
373
  __exports.ChangeService = ChangeService;