@sap-ux/preview-middleware 0.16.92 → 0.16.94
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.
- package/README.md +13 -13
- package/dist/client/cpe/changes/service.js +144 -38
- package/dist/client/cpe/changes/service.ts +253 -95
- package/dist/client/cpe/init.js +1 -1
- package/dist/client/cpe/init.ts +1 -1
- package/dist/client/cpe/outline/service.js +3 -1
- package/dist/client/cpe/outline/service.ts +3 -1
- package/dist/client/thirdparty/@sap-ux-private/control-property-editor-common.js +2 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ When this middleware is used together with the `reload-middleware`, then the ord
|
|
|
12
12
|
afterMiddleware: reload-middleware
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
## Configuration Options
|
|
15
|
+
## [Configuration Options](#configuration-options)
|
|
16
16
|
| Option | Type | Default Value | Description |
|
|
17
17
|
| ---------------------- | --------- | ---------------- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
18
18
|
| `flp` | | | Optional configuration object for the local Fiori launchpad |
|
|
@@ -32,7 +32,7 @@ When this middleware is used together with the `reload-middleware`, then the ord
|
|
|
32
32
|
| `test` | `array` | `undefined` | Optional list of configurations for automated testing. |
|
|
33
33
|
| `debug` | `boolean` | `false` | Enables debug output |
|
|
34
34
|
|
|
35
|
-
### `flp.apps`
|
|
35
|
+
### [`flp.apps`](#configuration-option-flpapps)
|
|
36
36
|
Array of additional application configurations:
|
|
37
37
|
| Option | Type | Default Value | Description |
|
|
38
38
|
| ------------------------ | -------- | -------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
@@ -42,7 +42,7 @@ Array of additional application configurations:
|
|
|
42
42
|
| `intent.object` | `string` | `(calculated)` | Optional intent object, if it is not provided then it will be calculated based on the application id |
|
|
43
43
|
| `intent.action` | `string` | `preview` | Optional intent action |
|
|
44
44
|
|
|
45
|
-
### `adp.target`
|
|
45
|
+
### [`adp.target`](#configuration-option-adptarget)
|
|
46
46
|
| Option | Type | Description |
|
|
47
47
|
| ------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
48
48
|
| `url` | `string` mandatory (local) | Mandatory URL pointing to the backend system. *Not required if destination is provided and the proxy is running SAP Business Application Studio |
|
|
@@ -50,13 +50,13 @@ Array of additional application configurations:
|
|
|
50
50
|
| `client` | `string` optional | sap-client parameter |
|
|
51
51
|
| `scp` | `boolean` optional | If set to true the proxy will execute the required OAuth routine for the ABAP environment on SAP BTP |
|
|
52
52
|
|
|
53
|
-
### `rta.editors`
|
|
53
|
+
### [`rta.editors`](#configuration-option-rtaeditors)
|
|
54
54
|
| Option | Type | Description |
|
|
55
55
|
| --------------- | -------------------| -----------------------------------------------------------------------------------------------|
|
|
56
56
|
| `path` | `string` mandatory | The mount point to be used for the editor. |
|
|
57
57
|
| `developerMode` | `boolean` optional | Enables/disables the runtime adaptation developer mode (only supported for adaptation projects) |
|
|
58
58
|
|
|
59
|
-
### `test`
|
|
59
|
+
### [`test`](#configuration-option-test)
|
|
60
60
|
| Option | Type | Description |
|
|
61
61
|
| --------------- | -------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
62
62
|
| `framework` | `string` mandatory | Currently `OPA5`, `QUnit` (only QUnit 2.3.2 provided as third-party module via [OpenUI5](https://github.com/SAP/openui5/blob/master/THIRDPARTY.txt)/SAPUI5) and `Testsuite` are supported. `Testsuite` will generate a testsuite for all configured frameworks that can be be used with a test runner (like e.g. karma) |
|
|
@@ -65,11 +65,11 @@ Array of additional application configurations:
|
|
|
65
65
|
| `pattern` | `string` optional | Optional glob pattern to find the tests. By default `/test/**/*Journey.*` is used for `OPA5` and `/test/**/*Test.*` is used for `QUnit` (n.a. for `Testsuite`) |
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
## Usage
|
|
68
|
+
## [Usage](#usage)
|
|
69
69
|
The middleware can be used without configuration. However, since the middleware intercepts a few requests that might otherwise be handled by a different middleware, it is strongly recommended to run other file serving middlewares after the `preview-middleware` e.g. `backend-proxy-middleware` and `ui5-proxy-middleware` (and the corresponding middlewares in the `@sap/ux-ui5-tooling`).
|
|
70
70
|
Example: [./test/fixtures/simple-app/ui5.yaml](./test/fixtures/simple-app/ui5.yaml)
|
|
71
71
|
|
|
72
|
-
### Minimal Configuration
|
|
72
|
+
### [Minimal Configuration](#minimal-configuration)
|
|
73
73
|
With no configuration provided, the local FLP will be available at `/test/flp.html` and the log level is `info`.
|
|
74
74
|
```Yaml
|
|
75
75
|
server:
|
|
@@ -78,7 +78,7 @@ server:
|
|
|
78
78
|
afterMiddleware: compression
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
### Different Path and Debugging enabled
|
|
81
|
+
### [Different Path and Debugging enabled](#different-path-and-debugging-enabled)
|
|
82
82
|
With this configuration, the local FLP will be available at `/test/myFLP.html` and the log level is `debug`.
|
|
83
83
|
```Yaml
|
|
84
84
|
server:
|
|
@@ -91,7 +91,7 @@ server:
|
|
|
91
91
|
debug: true
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
### Additional Applications
|
|
94
|
+
### [Additional Applications](#additional-applications)
|
|
95
95
|
If you want to test cross application navigation, then you can add additional applications into the local FLP.
|
|
96
96
|
With this configuration, an application that is locally available in `../local-folder` will be available at `/apps/other-app` and will also be added as tile to the local FLP as well as one of the UI5 sample apps will be bound to the intent `TheOther-preview`.
|
|
97
97
|
```Yaml
|
|
@@ -113,7 +113,7 @@ server:
|
|
|
113
113
|
action: preview
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
### Runtime Adaptation Support
|
|
116
|
+
### [Runtime Adaptation Support](#runtime-adaptation-support)
|
|
117
117
|
If you want to create variants as part of your application, then you can create an additional mount point allowing to create and edit variants.
|
|
118
118
|
```Yaml
|
|
119
119
|
server:
|
|
@@ -131,7 +131,7 @@ This mount path can be used with a run script that looks as follows.
|
|
|
131
131
|
"start-variants-management": "ui5 serve --open \"test/variant-editor.html.html#app-preview\""
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
### Test Suites
|
|
134
|
+
### [Test Suites](#test-suites)
|
|
135
135
|
If you want to also generate generic test suites and test runners for QUnit or OPA5 tests then you can use the following minimum configurations
|
|
136
136
|
```Yaml
|
|
137
137
|
server:
|
|
@@ -145,7 +145,7 @@ server:
|
|
|
145
145
|
- framework: OPA5
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
### Adaptation Project
|
|
148
|
+
### [Adaptation Project](#adaptation-project)
|
|
149
149
|
If you want to use the middleware in an adaption project, the additional `adp` object needs to be configured. This example would preview a local adaptation project merged with its reference application from the target system at `http://sap.example` and it will ignore certification validation errors. For adaptation projects, it is also recommended to add the `rta` configuration allowing to edit the project.
|
|
150
150
|
```Yaml
|
|
151
151
|
server:
|
|
@@ -170,7 +170,7 @@ When the middleware is used in an adaptation project together with a middleware
|
|
|
170
170
|
afterMiddleware: preview-middleware
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
-
### Programmatic Usage
|
|
173
|
+
### [Programmatic Usage](#programmatic-usage)
|
|
174
174
|
Alternatively you can use the underlying middleware fuction programmatically, e.g. for the case when you want to incorporate the `preview-middleware` functionality in your own middleware.
|
|
175
175
|
|
|
176
176
|
```typescript
|
|
@@ -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:
|
|
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
|
-
|
|
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 =>
|
|
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
|
|
159
|
-
allCommands.
|
|
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
|
-
|
|
164
|
-
|
|
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
|
-
|
|
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(
|
|
234
|
+
this.updateStack();
|
|
191
235
|
handleStackChange();
|
|
192
236
|
};
|
|
193
237
|
}
|
|
194
|
-
|
|
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
|
|
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 (!
|
|
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} =
|
|
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;
|
|
@@ -2,7 +2,8 @@ import type {
|
|
|
2
2
|
ExternalAction,
|
|
3
3
|
PendingChange,
|
|
4
4
|
SavedPropertyChange,
|
|
5
|
-
UnknownSavedChange
|
|
5
|
+
UnknownSavedChange,
|
|
6
|
+
SavedControlChange
|
|
6
7
|
} from '@sap-ux-private/control-property-editor-common';
|
|
7
8
|
import {
|
|
8
9
|
changeProperty,
|
|
@@ -16,13 +17,15 @@ import {
|
|
|
16
17
|
} from '@sap-ux-private/control-property-editor-common';
|
|
17
18
|
import { applyChange } from './flex-change';
|
|
18
19
|
import type { SelectionService } from '../selection';
|
|
19
|
-
|
|
20
20
|
import type { ActionSenderFunction, SubscribeFunction, UI5AdaptationOptions } from '../types';
|
|
21
21
|
import type Event from 'sap/ui/base/Event';
|
|
22
22
|
import type FlexCommand from 'sap/ui/rta/command/FlexCommand';
|
|
23
23
|
import Log from 'sap/base/Log';
|
|
24
24
|
import { modeAndStackChangeHandler } from '../rta-service';
|
|
25
|
+
import JsControlTreeModifier from 'sap/ui/core/util/reflection/JsControlTreeModifier';
|
|
26
|
+
import FlexChange from 'sap/ui/fl/Change';
|
|
25
27
|
import { getError } from '../../utils/error';
|
|
28
|
+
import { isLowerThanMinimalUi5Version, getUi5Version } from '../../utils/version';
|
|
26
29
|
import MessageToast from 'sap/m/MessageToast';
|
|
27
30
|
import { getTextBundle } from '../../i18n';
|
|
28
31
|
|
|
@@ -94,9 +97,12 @@ function modifyRTAErrorMessage(errorMessage: string, id: string, type: string):
|
|
|
94
97
|
* A Class of ChangeService
|
|
95
98
|
*/
|
|
96
99
|
export class ChangeService {
|
|
97
|
-
private savedChanges: SavedPropertyChange[] = [];
|
|
100
|
+
private savedChanges: SavedPropertyChange[] | UnknownSavedChange[] | SavedControlChange[] = [];
|
|
98
101
|
private changesRequiringReload = 0;
|
|
99
102
|
private sendAction: (action: ExternalAction) => void;
|
|
103
|
+
private pendingChanges: PendingChange[] = [];
|
|
104
|
+
private changedFiles: Record<string, object> = {};
|
|
105
|
+
private readonly eventStack: object[] = [];
|
|
100
106
|
/**
|
|
101
107
|
*
|
|
102
108
|
* @param options ui5 adaptation options.
|
|
@@ -159,11 +165,11 @@ export class ChangeService {
|
|
|
159
165
|
*
|
|
160
166
|
* @param pendingChanges Changes that are waiting to be saved
|
|
161
167
|
*/
|
|
162
|
-
private updateStack(
|
|
168
|
+
private updateStack() {
|
|
163
169
|
this.sendAction(
|
|
164
170
|
changeStackModified({
|
|
165
|
-
saved: this.savedChanges,
|
|
166
|
-
pending: pendingChanges
|
|
171
|
+
saved: this.savedChanges ?? [],
|
|
172
|
+
pending: this.pendingChanges ?? []
|
|
167
173
|
})
|
|
168
174
|
);
|
|
169
175
|
}
|
|
@@ -172,54 +178,80 @@ export class ChangeService {
|
|
|
172
178
|
* Fetches saved changes from the workspace and sorts them.
|
|
173
179
|
*/
|
|
174
180
|
private async fetchSavedChanges(): Promise<void> {
|
|
181
|
+
this.changedFiles = {};
|
|
175
182
|
const savedChangesResponse = await fetch(FlexChangesEndPoints.changes + `?_=${Date.now()}`);
|
|
176
183
|
const savedChanges = (await savedChangesResponse.json()) as SavedChangesResponse;
|
|
177
184
|
const changes = (
|
|
178
|
-
|
|
179
|
-
.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
185
|
+
(
|
|
186
|
+
await Promise.all(
|
|
187
|
+
Object.keys(savedChanges ?? {}).map(
|
|
188
|
+
async (
|
|
189
|
+
key
|
|
190
|
+
): Promise<SavedPropertyChange | UnknownSavedChange | SavedControlChange | undefined> => {
|
|
191
|
+
const change: Change = savedChanges[key];
|
|
192
|
+
let selectorId;
|
|
193
|
+
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');
|
|
209
|
+
}
|
|
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
|
+
} catch (error) {
|
|
225
|
+
// Gracefully handle change files with invalid content
|
|
226
|
+
if (change.fileName) {
|
|
227
|
+
this.changedFiles[change.fileName] = change;
|
|
228
|
+
const unknownChange: UnknownSavedChange = {
|
|
229
|
+
type: 'saved',
|
|
230
|
+
kind: 'unknown',
|
|
231
|
+
changeType: change.changeType,
|
|
232
|
+
fileName: change.fileName,
|
|
233
|
+
timestamp: new Date(change.creation).getTime()
|
|
234
|
+
};
|
|
235
|
+
if (change.creation) {
|
|
236
|
+
unknownChange.timestamp = new Date(change.creation).getTime();
|
|
237
|
+
}
|
|
238
|
+
if (selectorId) {
|
|
239
|
+
const controlChange: SavedControlChange = {
|
|
240
|
+
...unknownChange,
|
|
241
|
+
kind: 'control',
|
|
242
|
+
controlId: selectorId
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
return controlChange;
|
|
246
|
+
}
|
|
247
|
+
return unknownChange;
|
|
248
|
+
}
|
|
249
|
+
return undefined;
|
|
216
250
|
}
|
|
217
|
-
return unknownChange;
|
|
218
251
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
.filter((change) => !!change) as SavedPropertyChange[]
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
).filter((change) => !!change) as SavedPropertyChange[]
|
|
223
255
|
).sort((a, b) => b.timestamp - a.timestamp);
|
|
224
256
|
this.savedChanges = changes;
|
|
225
257
|
}
|
|
@@ -232,11 +264,19 @@ export class ChangeService {
|
|
|
232
264
|
*/
|
|
233
265
|
public async deleteChange(controlId: string, propertyName: string, fileName?: string): Promise<void> {
|
|
234
266
|
const filesToDelete = this.savedChanges
|
|
235
|
-
.filter((change) =>
|
|
236
|
-
fileName
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
267
|
+
.filter((change) => {
|
|
268
|
+
if (fileName) {
|
|
269
|
+
return fileName === change.fileName;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (change.kind === 'property') {
|
|
273
|
+
return change.controlId === controlId && change.propertyName === propertyName;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (change.kind === 'control') {
|
|
277
|
+
return change.controlId === controlId;
|
|
278
|
+
}
|
|
279
|
+
})
|
|
240
280
|
.map((change) =>
|
|
241
281
|
fetch(FlexChangesEndPoints.changes, {
|
|
242
282
|
method: 'DELETE',
|
|
@@ -261,67 +301,101 @@ export class ChangeService {
|
|
|
261
301
|
*/
|
|
262
302
|
private createOnStackChangeHandler(): (event: Event) => Promise<void> {
|
|
263
303
|
const handleStackChange = modeAndStackChangeHandler(this.sendAction, this.options.rta);
|
|
264
|
-
return async (): Promise<void> => {
|
|
304
|
+
return async (event): Promise<void> => {
|
|
305
|
+
const pendingChanges: PendingChange[] = [];
|
|
306
|
+
this.eventStack.push(event);
|
|
265
307
|
const stack = this.options.rta.getCommandStack();
|
|
266
308
|
const allCommands = stack.getCommands();
|
|
267
309
|
const executedCommands = stack.getAllExecutedCommands();
|
|
268
310
|
const inactiveCommandCount = allCommands.length - executedCommands.length;
|
|
269
|
-
let
|
|
270
|
-
|
|
311
|
+
let i: number, command: FlexCommand;
|
|
312
|
+
for ([i, command] of allCommands.entries()) {
|
|
271
313
|
try {
|
|
272
314
|
if (typeof command.getCommands === 'function') {
|
|
273
315
|
const subCommands = command.getCommands();
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (pendingChange) {
|
|
277
|
-
activeChanges.push(pendingChange);
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
} else {
|
|
281
|
-
const pendingChange = this.prepareChangeType(command, inactiveCommandCount, i);
|
|
282
|
-
if (pendingChange) {
|
|
283
|
-
activeChanges.push(pendingChange);
|
|
316
|
+
for (const subCommand of subCommands) {
|
|
317
|
+
await this.handleCommand(subCommand, inactiveCommandCount, i, pendingChanges);
|
|
284
318
|
}
|
|
319
|
+
} else {
|
|
320
|
+
await this.handleCommand(command, inactiveCommandCount, i, pendingChanges);
|
|
285
321
|
}
|
|
286
322
|
} catch (error) {
|
|
287
323
|
Log.error('CPE: Change creation Failed', getError(error));
|
|
288
324
|
}
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
activeChanges = activeChanges.filter((change): boolean => !!change);
|
|
292
|
-
const changesRequiringReload = activeChanges.reduce(
|
|
293
|
-
(sum, change) => (change.changeType === 'appdescr_fe_changePageConfiguration' ? sum + 1 : sum),
|
|
294
|
-
0
|
|
295
|
-
);
|
|
296
|
-
if (changesRequiringReload > this.changesRequiringReload) {
|
|
297
|
-
const resourceBundle = await getTextBundle();
|
|
298
|
-
MessageToast.show(resourceBundle.getText('CPE_CHANGES_VISIBLE_AFTER_SAVE_AND_RELOAD_MESSAGE'), {
|
|
299
|
-
duration: 8000
|
|
300
|
-
});
|
|
301
|
-
this.sendAction(setApplicationRequiresReload(changesRequiringReload > 0));
|
|
302
325
|
}
|
|
303
|
-
|
|
304
|
-
|
|
326
|
+
const resourceBundle = await getTextBundle();
|
|
327
|
+
const eventIndex = this.eventStack.indexOf(event);
|
|
328
|
+
if (this.eventStack.length - 1 === eventIndex) {
|
|
329
|
+
this.pendingChanges = pendingChanges.filter((change): boolean => !!change);
|
|
330
|
+
const changesRequiringReload = this.pendingChanges.reduce(
|
|
331
|
+
(sum, change) => (change.changeType === 'appdescr_fe_changePageConfiguration' ? sum + 1 : sum),
|
|
332
|
+
0
|
|
333
|
+
);
|
|
334
|
+
if (changesRequiringReload > this.changesRequiringReload) {
|
|
335
|
+
MessageToast.show(resourceBundle.getText('CPE_CHANGES_VISIBLE_AFTER_SAVE_AND_RELOAD_MESSAGE'), {
|
|
336
|
+
duration: 8000
|
|
337
|
+
});
|
|
338
|
+
this.sendAction(setApplicationRequiresReload(changesRequiringReload > 0));
|
|
339
|
+
}
|
|
340
|
+
this.changesRequiringReload = changesRequiringReload;
|
|
341
|
+
}
|
|
342
|
+
this.eventStack.splice(eventIndex, 1);
|
|
305
343
|
if (Array.isArray(allCommands) && allCommands.length === 0) {
|
|
344
|
+
this.pendingChanges = [];
|
|
306
345
|
await this.fetchSavedChanges();
|
|
307
346
|
}
|
|
308
|
-
|
|
309
|
-
this.updateStack(activeChanges);
|
|
347
|
+
this.updateStack();
|
|
310
348
|
handleStackChange();
|
|
311
349
|
};
|
|
312
350
|
}
|
|
313
351
|
|
|
314
|
-
|
|
352
|
+
/**
|
|
353
|
+
* Handles a command by preparing a pending change and adding it to the list of pending changes.
|
|
354
|
+
*
|
|
355
|
+
* @param {FlexCommand} command - The command to process.
|
|
356
|
+
* @param {number} inactiveCommandCount - The number of inactive commands.
|
|
357
|
+
* @param {number} index - The index of the current command being processed.
|
|
358
|
+
* @param {PendingChange[]} pendingChanges - The list of pending changes to update.
|
|
359
|
+
* @returns {Promise<void>} A promise that resolves when the command is handled.
|
|
360
|
+
*/
|
|
361
|
+
private async handleCommand(
|
|
362
|
+
command: FlexCommand,
|
|
363
|
+
inactiveCommandCount: number,
|
|
364
|
+
index: number,
|
|
365
|
+
pendingChanges: PendingChange[]
|
|
366
|
+
): Promise<void> {
|
|
367
|
+
const pendingChange = await this.prepareChangeType(command, inactiveCommandCount, index);
|
|
368
|
+
if (pendingChange) {
|
|
369
|
+
pendingChanges.push(pendingChange);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Prepares the type of change based on the command and other parameters.
|
|
375
|
+
*
|
|
376
|
+
* @param {FlexCommand} command - The command to process.
|
|
377
|
+
* @param {number} inactiveCommandCount - The number of inactive commands.
|
|
378
|
+
* @param {number} index - The index of the current command being processed.
|
|
379
|
+
* @returns {Promise<PendingChange | undefined>} - A promise that resolves to a `PendingChange` or `undefined`.
|
|
380
|
+
*/
|
|
381
|
+
private async prepareChangeType(
|
|
315
382
|
command: FlexCommand,
|
|
316
383
|
inactiveCommandCount: number,
|
|
317
384
|
index: number
|
|
318
|
-
): PendingChange | undefined {
|
|
385
|
+
): Promise<PendingChange | undefined> {
|
|
319
386
|
let result: PendingChange;
|
|
320
387
|
let value = '';
|
|
321
|
-
|
|
388
|
+
|
|
389
|
+
const change = command.getPreparedChange();
|
|
390
|
+
|
|
391
|
+
const selectorId =
|
|
392
|
+
typeof change.getSelector === 'function'
|
|
393
|
+
? await this.getControlIdByChange(change)
|
|
394
|
+
: this.getCommandSelectorId(command);
|
|
395
|
+
|
|
322
396
|
const changeType = this.getCommandChangeType(command);
|
|
323
397
|
|
|
324
|
-
if (!
|
|
398
|
+
if (!changeType) {
|
|
325
399
|
return undefined;
|
|
326
400
|
}
|
|
327
401
|
|
|
@@ -333,8 +407,9 @@ export class ChangeService {
|
|
|
333
407
|
value = command.getProperty('newBinding') as string;
|
|
334
408
|
break;
|
|
335
409
|
}
|
|
336
|
-
|
|
337
|
-
|
|
410
|
+
|
|
411
|
+
const { fileName } = change.getDefinition();
|
|
412
|
+
if ((changeType === 'propertyChange' || changeType === 'propertyBindingChange') && selectorId) {
|
|
338
413
|
result = {
|
|
339
414
|
type: 'pending',
|
|
340
415
|
kind: 'property',
|
|
@@ -350,15 +425,18 @@ export class ChangeService {
|
|
|
350
425
|
result = {
|
|
351
426
|
type: 'pending',
|
|
352
427
|
kind: 'unknown',
|
|
353
|
-
controlId: selectorId,
|
|
354
428
|
changeType,
|
|
355
429
|
isActive: index >= inactiveCommandCount,
|
|
356
|
-
controlName:
|
|
357
|
-
changeType === 'addXMLAtExtensionPoint'
|
|
358
|
-
? command.getSelector().name ?? ''
|
|
359
|
-
: command.getElement().getMetadata().getName().split('.').pop() ?? '',
|
|
360
430
|
fileName
|
|
361
431
|
};
|
|
432
|
+
|
|
433
|
+
if (selectorId) {
|
|
434
|
+
result = {
|
|
435
|
+
...result,
|
|
436
|
+
kind: 'control',
|
|
437
|
+
controlId: selectorId
|
|
438
|
+
};
|
|
439
|
+
}
|
|
362
440
|
}
|
|
363
441
|
|
|
364
442
|
return result;
|
|
@@ -414,4 +492,84 @@ export class ChangeService {
|
|
|
414
492
|
() => command.getParent()?.getElement().getId()
|
|
415
493
|
]) as string | undefined;
|
|
416
494
|
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Get element id by change.
|
|
498
|
+
*
|
|
499
|
+
* @param change to be executed for creating change
|
|
500
|
+
* @returns element id or empty string
|
|
501
|
+
*/
|
|
502
|
+
private async getControlIdByChange(change: FlexChange<ChangeContent>): Promise<string | undefined> {
|
|
503
|
+
const appComponent = this.options.rta.getRootControlInstance();
|
|
504
|
+
const selector = typeof change.getSelector === 'function' ? change.getSelector() : undefined;
|
|
505
|
+
const changeType = change.getChangeType();
|
|
506
|
+
const layer = change.getLayer();
|
|
507
|
+
|
|
508
|
+
if (!selector?.id) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
try {
|
|
513
|
+
let control = JsControlTreeModifier.bySelector(selector, appComponent);
|
|
514
|
+
if (!control) {
|
|
515
|
+
return selector.id;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const changeHandlerAPI = (await import('sap/ui/fl/write/api/ChangesWriteAPI')).default;
|
|
519
|
+
|
|
520
|
+
const changeHandler = await changeHandlerAPI.getChangeHandler({
|
|
521
|
+
changeType,
|
|
522
|
+
element: control,
|
|
523
|
+
modifier: JsControlTreeModifier,
|
|
524
|
+
layer
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
if (changeHandler && typeof changeHandler.getChangeVisualizationInfo === 'function') {
|
|
528
|
+
const result: { affectedControls?: [string] } = await changeHandler.getChangeVisualizationInfo(
|
|
529
|
+
change,
|
|
530
|
+
appComponent
|
|
531
|
+
);
|
|
532
|
+
return JsControlTreeModifier.getControlIdBySelector(
|
|
533
|
+
result?.affectedControls?.[0] ?? selector,
|
|
534
|
+
appComponent
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return JsControlTreeModifier.getControlIdBySelector(selector, appComponent);
|
|
539
|
+
} catch (error) {
|
|
540
|
+
Log.error('Getting element ID from change has failed:', getError(error));
|
|
541
|
+
return selector.id;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Sync outline changes to place modification markers when outline is changed.
|
|
547
|
+
*
|
|
548
|
+
* @returns void
|
|
549
|
+
*/
|
|
550
|
+
public async syncOutlineChanges(): Promise<void> {
|
|
551
|
+
for (const change of this.savedChanges) {
|
|
552
|
+
if (change.kind !== 'unknown') {
|
|
553
|
+
const flexObject = await this.getFlexObject(this.changedFiles[change.fileName]);
|
|
554
|
+
change.controlId = (await this.getControlIdByChange(flexObject)) ?? '';
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
this.updateStack();
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Get FlexObject from change object based on UI5 version.
|
|
562
|
+
*
|
|
563
|
+
* @param change change object
|
|
564
|
+
* @returns FlexChange
|
|
565
|
+
*/
|
|
566
|
+
private async getFlexObject(change: object): Promise<FlexChange<ChangeContent>> {
|
|
567
|
+
if (isLowerThanMinimalUi5Version(await getUi5Version(), { major: 1, minor: 109 })) {
|
|
568
|
+
const Change = (await import('sap/ui/fl/Change')).default;
|
|
569
|
+
return new Change(change);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const FlexObjectFactory = (await import('sap/ui/fl/apply/_internal/flexObjects/FlexObjectFactory')).default;
|
|
573
|
+
return FlexObjectFactory.createFromFileContent(change) as FlexChange<ChangeContent>;
|
|
574
|
+
}
|
|
417
575
|
}
|
package/dist/client/cpe/init.js
CHANGED
|
@@ -41,7 +41,7 @@ sap.ui.define([
|
|
|
41
41
|
const changesService = new ChangeService({ rta }, selectionService);
|
|
42
42
|
const connectorService = new WorkspaceConnectorService();
|
|
43
43
|
const rtaService = new RtaService(rta);
|
|
44
|
-
const outlineService = new OutlineService(rta);
|
|
44
|
+
const outlineService = new OutlineService(rta, changesService);
|
|
45
45
|
const quickActionService = new QuickActionService(rta, outlineService, registries);
|
|
46
46
|
const services = [
|
|
47
47
|
connectorService,
|
package/dist/client/cpe/init.ts
CHANGED
|
@@ -45,7 +45,7 @@ export default function init(
|
|
|
45
45
|
const changesService = new ChangeService({ rta }, selectionService);
|
|
46
46
|
const connectorService = new WorkspaceConnectorService();
|
|
47
47
|
const rtaService = new RtaService(rta);
|
|
48
|
-
const outlineService = new OutlineService(rta);
|
|
48
|
+
const outlineService = new OutlineService(rta, changesService);
|
|
49
49
|
const quickActionService = new QuickActionService(rta, outlineService, registries);
|
|
50
50
|
const services: Service[] = [
|
|
51
51
|
connectorService,
|
|
@@ -15,9 +15,10 @@ sap.ui.define([
|
|
|
15
15
|
const transformNodes = ___nodes['transformNodes'];
|
|
16
16
|
const OUTLINE_CHANGE_EVENT = 'OUTLINE_CHANGED';
|
|
17
17
|
class OutlineService extends EventTarget {
|
|
18
|
-
constructor(rta) {
|
|
18
|
+
constructor(rta, changeService) {
|
|
19
19
|
super();
|
|
20
20
|
this.rta = rta;
|
|
21
|
+
this.changeService = changeService;
|
|
21
22
|
}
|
|
22
23
|
async init(sendAction) {
|
|
23
24
|
const outline = await this.rta.getService('outline');
|
|
@@ -32,6 +33,7 @@ sap.ui.define([
|
|
|
32
33
|
const viewNodes = await outline.get();
|
|
33
34
|
const controlIndex = {};
|
|
34
35
|
const outlineNodes = await transformNodes(viewNodes, scenario, reuseComponentsIds, controlIndex);
|
|
36
|
+
await this.changeService.syncOutlineChanges();
|
|
35
37
|
const event = new CustomEvent(OUTLINE_CHANGE_EVENT, { detail: { controlIndex } });
|
|
36
38
|
this.dispatchEvent(event);
|
|
37
39
|
sendAction(outlineChanged(outlineNodes));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Log from 'sap/base/Log';
|
|
2
2
|
import type RuntimeAuthoring from 'sap/ui/rta/RuntimeAuthoring';
|
|
3
3
|
import type RTAOutlineService from 'sap/ui/rta/command/OutlineService';
|
|
4
|
+
import type { ChangeService } from '../changes/service';
|
|
4
5
|
|
|
5
6
|
import type { ExternalAction } from '@sap-ux-private/control-property-editor-common';
|
|
6
7
|
import { outlineChanged, SCENARIO, showMessage } from '@sap-ux-private/control-property-editor-common';
|
|
@@ -19,7 +20,7 @@ export interface OutlineChangedEventDetail {
|
|
|
19
20
|
* A Class of WorkspaceConnectorService
|
|
20
21
|
*/
|
|
21
22
|
export class OutlineService extends EventTarget {
|
|
22
|
-
constructor(private rta: RuntimeAuthoring) {
|
|
23
|
+
constructor(private readonly rta: RuntimeAuthoring, private readonly changeService: ChangeService) {
|
|
23
24
|
super();
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -41,6 +42,7 @@ export class OutlineService extends EventTarget {
|
|
|
41
42
|
const viewNodes = await outline.get();
|
|
42
43
|
const controlIndex: ControlTreeIndex = {};
|
|
43
44
|
const outlineNodes = await transformNodes(viewNodes, scenario, reuseComponentsIds, controlIndex);
|
|
45
|
+
await this.changeService.syncOutlineChanges();
|
|
44
46
|
|
|
45
47
|
const event = new CustomEvent(OUTLINE_CHANGE_EVENT, {
|
|
46
48
|
detail: {
|
|
@@ -8,7 +8,7 @@ sap.ui.define((function () { 'use strict';
|
|
|
8
8
|
|
|
9
9
|
(function (exports) {
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.setApplicationRequiresReload = exports.executeQuickAction = exports.updateQuickAction = exports.quickActionListChanged = exports.save = exports.redo = exports.undo = exports.appLoaded = exports.setSaveEnablement = exports.setUndoRedoEnablement = exports.applicationModeChanged = exports.setAppMode = exports.storageFileChanged = exports.reloadApplication = exports.showMessage = exports.changeStackModified = exports.propertyChangeFailed = exports.propertyChanged = exports.changeProperty = exports.outlineChanged = exports.deletePropertyChanges = exports.addExtensionPoint = exports.selectControl = exports.controlSelected = exports.iconsLoaded = exports.EXTERNAL_ACTION_PREFIX = exports.NESTED_QUICK_ACTION_KIND = exports.SIMPLE_QUICK_ACTION_KIND = exports.UNKNOWN_CHANGE_KIND = exports.PROPERTY_CHANGE_KIND = exports.SAVED_CHANGE_TYPE = exports.PENDING_CHANGE_TYPE = exports.SCENARIO = exports.CHECKBOX_EDITOR_TYPE = exports.DROPDOWN_EDITOR_TYPE = exports.INPUT_EDITOR_TYPE = exports.STRING_VALUE_TYPE = exports.FLOAT_VALUE_TYPE = exports.INTEGER_VALUE_TYPE = exports.BOOLEAN_VALUE_TYPE = void 0;
|
|
11
|
+
exports.setApplicationRequiresReload = exports.executeQuickAction = exports.updateQuickAction = exports.quickActionListChanged = exports.save = exports.redo = exports.undo = exports.appLoaded = exports.setSaveEnablement = exports.setUndoRedoEnablement = exports.applicationModeChanged = exports.setAppMode = exports.storageFileChanged = exports.reloadApplication = exports.showMessage = exports.changeStackModified = exports.propertyChangeFailed = exports.propertyChanged = exports.changeProperty = exports.outlineChanged = exports.deletePropertyChanges = exports.addExtensionPoint = exports.selectControl = exports.controlSelected = exports.iconsLoaded = exports.EXTERNAL_ACTION_PREFIX = exports.NESTED_QUICK_ACTION_KIND = exports.SIMPLE_QUICK_ACTION_KIND = exports.CONTROL_CHANGE_KIND = exports.UNKNOWN_CHANGE_KIND = exports.PROPERTY_CHANGE_KIND = exports.SAVED_CHANGE_TYPE = exports.PENDING_CHANGE_TYPE = exports.SCENARIO = exports.CHECKBOX_EDITOR_TYPE = exports.DROPDOWN_EDITOR_TYPE = exports.INPUT_EDITOR_TYPE = exports.STRING_VALUE_TYPE = exports.FLOAT_VALUE_TYPE = exports.INTEGER_VALUE_TYPE = exports.BOOLEAN_VALUE_TYPE = void 0;
|
|
12
12
|
exports.BOOLEAN_VALUE_TYPE = 'boolean';
|
|
13
13
|
exports.INTEGER_VALUE_TYPE = 'integer';
|
|
14
14
|
exports.FLOAT_VALUE_TYPE = 'float';
|
|
@@ -27,6 +27,7 @@ sap.ui.define((function () { 'use strict';
|
|
|
27
27
|
exports.SAVED_CHANGE_TYPE = 'saved';
|
|
28
28
|
exports.PROPERTY_CHANGE_KIND = 'property';
|
|
29
29
|
exports.UNKNOWN_CHANGE_KIND = 'unknown';
|
|
30
|
+
exports.CONTROL_CHANGE_KIND = 'control';
|
|
30
31
|
exports.SIMPLE_QUICK_ACTION_KIND = 'simple';
|
|
31
32
|
exports.NESTED_QUICK_ACTION_KIND = 'nested';
|
|
32
33
|
/**
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"bugs": {
|
|
10
10
|
"url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Apreview-middleware"
|
|
11
11
|
},
|
|
12
|
-
"version": "0.16.
|
|
12
|
+
"version": "0.16.94",
|
|
13
13
|
"license": "Apache-2.0",
|
|
14
14
|
"author": "@SAP/ux-tools-team",
|
|
15
15
|
"main": "dist/index.js",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@sap-ux/feature-toggle": "0.2.2",
|
|
30
30
|
"@sap-ux/btp-utils": "0.15.2",
|
|
31
31
|
"@sap-ux/adp-tooling": "0.12.62",
|
|
32
|
-
"@sap-ux/control-property-editor-sources": "npm:@sap-ux/control-property-editor@0.5.
|
|
32
|
+
"@sap-ux/control-property-editor-sources": "npm:@sap-ux/control-property-editor@0.5.22",
|
|
33
33
|
"@sap-ux/project-access": "1.28.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"supertest": "6.3.3",
|
|
47
47
|
"@sap-ux-private/playwright": "0.1.0",
|
|
48
48
|
"dotenv": "16.3.1",
|
|
49
|
-
"@private/preview-middleware-client": "npm:@sap-ux-private/preview-middleware-client@0.11.
|
|
49
|
+
"@private/preview-middleware-client": "npm:@sap-ux-private/preview-middleware-client@0.11.18",
|
|
50
50
|
"@sap-ux/axios-extension": "1.16.7",
|
|
51
51
|
"@sap-ux/store": "0.9.2",
|
|
52
52
|
"@sap-ux/ui5-info": "0.8.1",
|