@sap-ux/preview-middleware 0.8.7 → 0.10.0

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.
@@ -1,7 +1,7 @@
1
1
  import type {
2
2
  ExternalAction,
3
+ PendingChange,
3
4
  SavedPropertyChange,
4
- PendingPropertyChange,
5
5
  UnknownSavedChange
6
6
  } from '@sap-ux-private/control-property-editor-common';
7
7
  import {
@@ -39,6 +39,7 @@ interface Change {
39
39
  selector: ChangeSelector;
40
40
  content: ChangeContent;
41
41
  creation: string;
42
+ changeType: string;
42
43
  }
43
44
 
44
45
  type Properties<T extends object> = { [K in keyof T]-?: K extends string ? K : never }[keyof T];
@@ -91,10 +92,7 @@ export class ChangeService {
91
92
  * @param ui5 facade for ui5 framework methods.
92
93
  * @param selectionService selection service instance.
93
94
  */
94
- constructor(
95
- private readonly options: UI5AdaptationOptions,
96
- private readonly selectionService: SelectionService
97
- ) {}
95
+ constructor(private readonly options: UI5AdaptationOptions, private readonly selectionService: SelectionService) {}
98
96
 
99
97
  /**
100
98
  *
@@ -144,6 +142,9 @@ export class ChangeService {
144
142
  ) {
145
143
  throw new Error('Invalid change, missing new value in the change file');
146
144
  }
145
+ if (change.changeType !== 'propertyChange' && change.changeType !== 'propertyBindingChange') {
146
+ throw new Error('Unknown Change Type');
147
+ }
147
148
  return {
148
149
  type: 'saved',
149
150
  kind: 'valid',
@@ -152,7 +153,8 @@ export class ChangeService {
152
153
  propertyName: change.content.property,
153
154
  value: change.content.newValue ?? change.content.newBinding,
154
155
  timestamp: new Date(change.creation).getTime(),
155
- controlName: change.selector.type.split('.').pop()
156
+ controlName: change.selector.type ? (change.selector.type.split('.').pop() as string) : '',
157
+ changeType: change.changeType
156
158
  };
157
159
  } catch (error) {
158
160
  // Gracefully handle change files with invalid content
@@ -160,7 +162,8 @@ export class ChangeService {
160
162
  const unknownChange: UnknownSavedChange = {
161
163
  type: 'saved',
162
164
  kind: 'unknown',
163
- fileName: change.fileName
165
+ fileName: change.fileName,
166
+ controlId: change.selector?.id // some changes may not have selector
164
167
  };
165
168
  if (change.creation) {
166
169
  unknownChange.timestamp = new Date(change.creation).getTime();
@@ -220,38 +223,23 @@ export class ChangeService {
220
223
  const allCommands = stack.getCommands();
221
224
  const executedCommands = stack.getAllExecutedCommands();
222
225
  const inactiveCommandCount = allCommands.length - executedCommands.length;
223
-
224
- const activeChanges = allCommands
225
- .map((command: BaseCommand, i): PendingPropertyChange | undefined => {
226
- let result: PendingPropertyChange | undefined;
227
- try {
228
- const selector = command.getProperty('selector');
229
- const changeType = command.getProperty('changeType');
230
- let value = '';
231
- switch (changeType) {
232
- case 'propertyChange':
233
- value = command.getProperty('newValue');
234
- break;
235
- case 'propertyBindingChange':
236
- value = command.getProperty('newBinding');
237
- break;
238
- default:
239
- throw new Error(`Invalid changeType ${changeType}`);
240
- }
241
- result = {
242
- type: 'pending',
243
- controlId: selector.id,
244
- propertyName: command.getProperty('propertyName'),
245
- isActive: i >= inactiveCommandCount,
246
- value,
247
- controlName: command.getElement().getMetadata().getName().split('.').pop() ?? ''
248
- };
249
- } catch (error) {
250
- Log.error('Failed: ', error);
226
+ let activeChanges: PendingChange[] = [];
227
+ allCommands.forEach((command: BaseCommand, i): void => {
228
+ try {
229
+ if (typeof command.getCommands === 'function') {
230
+ const subCommands = command.getCommands();
231
+ subCommands.forEach((command) => {
232
+ activeChanges.push(this.prepareChangeType(command, inactiveCommandCount, i));
233
+ });
234
+ } else {
235
+ activeChanges.push(this.prepareChangeType(command, inactiveCommandCount, i));
251
236
  }
252
- return result;
253
- })
254
- .filter((change): boolean => !!change) as PendingPropertyChange[];
237
+ } catch (error) {
238
+ Log.error('CPE: Change creation Failed', error);
239
+ }
240
+ });
241
+
242
+ activeChanges = activeChanges.filter((change): boolean => !!change);
255
243
  sendAction(
256
244
  changeStackModified({
257
245
  saved: this.savedChanges,
@@ -260,4 +248,40 @@ export class ChangeService {
260
248
  );
261
249
  };
262
250
  }
251
+
252
+ private prepareChangeType(command: BaseCommand, inactiveCommandCount: number, index: number): PendingChange {
253
+ let result: PendingChange;
254
+ let value = '';
255
+ const selector = command.getSelector();
256
+ const changeType = command.getChangeType();
257
+ switch (changeType) {
258
+ case 'propertyChange':
259
+ value = command.getProperty('newValue');
260
+ break;
261
+ case 'propertyBindingChange':
262
+ value = command.getProperty('newBinding');
263
+ break;
264
+ }
265
+ if (changeType === 'propertyChange' || changeType === 'propertyBindingChange') {
266
+ result = {
267
+ type: 'pending',
268
+ changeType,
269
+ controlId: selector.id,
270
+ propertyName: command.getProperty('propertyName'),
271
+ isActive: index >= inactiveCommandCount,
272
+ value,
273
+ controlName: command.getElement().getMetadata().getName().split('.').pop() ?? ''
274
+ };
275
+ } else {
276
+ result = {
277
+ type: 'pending',
278
+ controlId: selector.id,
279
+ changeType,
280
+ isActive: index >= inactiveCommandCount,
281
+ controlName: command.getElement().getMetadata().getName().split('.').pop() ?? ''
282
+ };
283
+ }
284
+
285
+ return result;
286
+ }
263
287
  }
@@ -9,10 +9,11 @@ sap.ui.define([
9
9
  const transformNodes = ___nodes['transformNodes'];
10
10
  async function initOutline(rta, sendAction) {
11
11
  const outline = await rta.getService('outline');
12
+ const scenario = rta.getFlexSettings().scenario;
12
13
  async function syncOutline() {
13
14
  try {
14
15
  const viewNodes = await outline.get();
15
- const outlineNodes = await transformNodes(viewNodes);
16
+ const outlineNodes = await transformNodes(viewNodes, scenario);
16
17
  sendAction(outlineChanged(outlineNodes));
17
18
  } catch (error) {
18
19
  Log.error('Outline sync failed!', error);
@@ -14,10 +14,11 @@ import Log from 'sap/base/Log';
14
14
  */
15
15
  export async function initOutline(rta: RuntimeAuthoring, sendAction: (action: ExternalAction) => void): Promise<void> {
16
16
  const outline = await rta.getService<OutlineService>('outline');
17
+ const scenario = rta.getFlexSettings().scenario;
17
18
  async function syncOutline() {
18
19
  try {
19
20
  const viewNodes = await outline.get();
20
- const outlineNodes = await transformNodes(viewNodes);
21
+ const outlineNodes = await transformNodes(viewNodes, scenario);
21
22
 
22
23
  sendAction(outlineChanged(outlineNodes));
23
24
  } catch (error) {
@@ -4,6 +4,12 @@ sap.ui.define(["./utils"], function (___utils) {
4
4
  "use strict";
5
5
 
6
6
  const isEditable = ___utils["isEditable"];
7
+ /**
8
+ * Retrieves additional data for a given control ID.
9
+ *
10
+ * @param id The control ID for which to retrieve additional data
11
+ * @returns An object containing additional data, including an optional 'text' property
12
+ */
7
13
  function getAdditionalData(id) {
8
14
  const control = sap.ui.getCore().byId(id);
9
15
  if (control?.getMetadata().getProperty('text')) {
@@ -17,31 +23,54 @@ sap.ui.define(["./utils"], function (___utils) {
17
23
  return {};
18
24
  }
19
25
 
26
+ /**
27
+ * Gets the children nodes of an aggregation type node.
28
+ *
29
+ * @param current The current node to retrieve children from
30
+ * @returns An array of children nodes, or an empty array if none are found
31
+ */
32
+ function getChildren(current) {
33
+ return (current.elements ?? []).flatMap(element => element.type === 'aggregation' ? element.elements ?? [] : []);
34
+ }
35
+
20
36
  /**
21
37
  * Transform node.
22
38
  *
23
39
  * @param input outline view node
24
- * @param getAdditionalData gets additional data for give control id.
40
+ * @param scenario type of project
25
41
  * @returns Promise<OutlineNode[]>
26
42
  */
27
- async function transformNodes(input) {
43
+ async function transformNodes(input, scenario) {
28
44
  const stack = [...input];
29
45
  const items = [];
30
46
  while (stack.length) {
31
47
  const current = stack.shift();
32
48
  const editable = await isEditable(current?.id);
33
49
  if (current?.type === 'element') {
34
- const children = (current.elements ?? []).flatMap(element => element.type === 'aggregation' ? element.elements ?? [] : []);
50
+ const children = getChildren(current);
35
51
  const {
36
52
  text
37
53
  } = getAdditionalData(current.id);
54
+ const technicalName = current.technicalName.split('.').slice(-1)[0];
55
+ const node = {
56
+ controlId: current.id,
57
+ controlType: current.technicalName,
58
+ name: text ?? technicalName,
59
+ editable,
60
+ visible: current.visible ?? true,
61
+ children: await transformNodes(children, scenario)
62
+ };
63
+ items.push(node);
64
+ }
65
+ if (scenario === 'ADAPTATION_PROJECT' && current?.type === 'extensionPoint') {
38
66
  const node = {
39
67
  controlId: current.id,
40
68
  controlType: current.technicalName,
41
- name: text ?? current.technicalName.split('.').slice(-1)[0],
69
+ name: current.name,
42
70
  editable,
43
71
  visible: current.visible ?? true,
44
- children: await transformNodes(children)
72
+ children: [],
73
+ icon: current.icon
45
74
  };
46
75
  items.push(node);
47
76
  }
@@ -1,7 +1,16 @@
1
1
  import type { OutlineNode } from '@sap-ux-private/control-property-editor-common';
2
- import { isEditable } from './utils';
2
+
3
3
  import type { OutlineViewNode } from 'sap/ui/rta/command/OutlineService';
4
+ import type { Scenario } from 'sap/ui/fl/Scenario';
5
+
6
+ import { isEditable } from './utils';
4
7
 
8
+ /**
9
+ * Retrieves additional data for a given control ID.
10
+ *
11
+ * @param id The control ID for which to retrieve additional data
12
+ * @returns An object containing additional data, including an optional 'text' property
13
+ */
5
14
  function getAdditionalData(id: string): { text?: string } {
6
15
  const control = sap.ui.getCore().byId(id);
7
16
  if (control?.getMetadata().getProperty('text')) {
@@ -15,32 +24,57 @@ function getAdditionalData(id: string): { text?: string } {
15
24
  return {};
16
25
  }
17
26
 
27
+ /**
28
+ * Gets the children nodes of an aggregation type node.
29
+ *
30
+ * @param current The current node to retrieve children from
31
+ * @returns An array of children nodes, or an empty array if none are found
32
+ */
33
+ function getChildren(current: OutlineViewNode): OutlineViewNode[] {
34
+ return (current.elements ?? []).flatMap((element: OutlineViewNode) =>
35
+ element.type === 'aggregation' ? element.elements ?? [] : []
36
+ );
37
+ }
38
+
18
39
  /**
19
40
  * Transform node.
20
41
  *
21
42
  * @param input outline view node
22
- * @param getAdditionalData gets additional data for give control id.
43
+ * @param scenario type of project
23
44
  * @returns Promise<OutlineNode[]>
24
45
  */
25
- export async function transformNodes(input: OutlineViewNode[]): Promise<OutlineNode[]> {
46
+ export async function transformNodes(input: OutlineViewNode[], scenario: Scenario): Promise<OutlineNode[]> {
26
47
  const stack = [...input];
27
48
  const items: OutlineNode[] = [];
28
49
  while (stack.length) {
29
50
  const current = stack.shift();
30
51
  const editable = await isEditable(current?.id);
31
52
  if (current?.type === 'element') {
32
- const children = (current.elements ?? []).flatMap((element: OutlineViewNode) =>
33
- element.type === 'aggregation' ? element.elements ?? [] : []
34
- );
53
+ const children = getChildren(current);
35
54
  const { text } = getAdditionalData(current.id);
55
+ const technicalName = current.technicalName.split('.').slice(-1)[0];
56
+
57
+ const node: OutlineNode = {
58
+ controlId: current.id,
59
+ controlType: current.technicalName,
60
+ name: text ?? technicalName,
61
+ editable,
62
+ visible: current.visible ?? true,
63
+ children: await transformNodes(children, scenario)
64
+ };
65
+
66
+ items.push(node);
67
+ }
36
68
 
69
+ if (scenario === 'ADAPTATION_PROJECT' && current?.type === 'extensionPoint') {
37
70
  const node: OutlineNode = {
38
71
  controlId: current.id,
39
72
  controlType: current.technicalName,
40
- name: text ?? current.technicalName.split('.').slice(-1)[0],
73
+ name: current.name!,
41
74
  editable,
42
75
  visible: current.visible ?? true,
43
- children: await transformNodes(children)
76
+ children: [],
77
+ icon: current.icon
44
78
  };
45
79
 
46
80
  items.push(node);
@@ -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.changeStackModified = exports.propertyChangeFailed = exports.propertyChanged = exports.changeProperty = exports.outlineChanged = exports.deletePropertyChanges = exports.selectControl = exports.controlSelected = exports.iconsLoaded = exports.EXTERNAL_ACTION_PREFIX = 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.changeStackModified = exports.propertyChangeFailed = exports.propertyChanged = exports.changeProperty = exports.outlineChanged = exports.deletePropertyChanges = exports.addExtensionPoint = exports.selectControl = exports.controlSelected = exports.iconsLoaded = exports.EXTERNAL_ACTION_PREFIX = 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';
@@ -57,6 +57,7 @@ sap.ui.define((function () { 'use strict';
57
57
  exports.iconsLoaded = createExternalAction('icons-loaded');
58
58
  exports.controlSelected = createExternalAction('control-selected');
59
59
  exports.selectControl = createExternalAction('select-control');
60
+ exports.addExtensionPoint = createExternalAction('add-extension-point');
60
61
  exports.deletePropertyChanges = createExternalAction('delete-property-changes');
61
62
  exports.outlineChanged = createExternalAction('outline-changed');
62
63
  exports.changeProperty = createExternalAction('change-property');
@@ -224,9 +225,47 @@ sap.ui.define((function () { 'use strict';
224
225
  (function (exports) {
225
226
  Object.defineProperty(exports, "__esModule", { value: true });
226
227
  exports.FlexChangesEndPoints = exports.convertCamelCaseToPascalCase = void 0;
228
+ const isUpperCase = (code) => code >= 65 && code <= 90;
229
+ const isLowerCase = (code) => code >= 97 && code <= 122;
230
+ const isWordDelimiter = (code) => code === 45 || code === 95 || code === 32; // - or _ or space
227
231
  const convertCamelCaseToPascalCase = (text) => {
228
- const string = text.replace(/([A-Z])/g, ' $1');
229
- return string.charAt(0).toUpperCase() + string.slice(1);
232
+ const words = [];
233
+ let word = '';
234
+ let lookForUpperCase = true;
235
+ for (let i = 0; i < text.length; i++) {
236
+ const character = text[i];
237
+ if (lookForUpperCase) {
238
+ // make sure that the first letter is capitalized
239
+ word += word.length === 0 ? character.toUpperCase() : character;
240
+ if (isLowerCase(text.charCodeAt(i + 1))) {
241
+ // First lower case character after upper case character -> switch mode to collect only lower case characters
242
+ lookForUpperCase = false;
243
+ }
244
+ else if (isUpperCase(text.charCodeAt(i + 1)) && isLowerCase(text.charCodeAt(i + 2))) {
245
+ // Next character is the last uppercase character after a sequence of upper case character -> create an abbreviated word
246
+ words.push(word);
247
+ word = '';
248
+ }
249
+ }
250
+ else if (isUpperCase(text.charCodeAt(i))) {
251
+ // Upper case character indicates the beginning of a new word -> switch mode to detect abbreviated word
252
+ words.push(word);
253
+ lookForUpperCase = true;
254
+ word = character;
255
+ }
256
+ else if (isWordDelimiter(text.charCodeAt(i))) {
257
+ words.push(word);
258
+ lookForUpperCase = true;
259
+ word = '';
260
+ }
261
+ else {
262
+ word += character;
263
+ }
264
+ }
265
+ if (word.length) {
266
+ words.push(word);
267
+ }
268
+ return words.join(' ');
230
269
  };
231
270
  exports.convertCamelCaseToPascalCase = convertCamelCaseToPascalCase;
232
271
  (function (FlexChangesEndPoints) {
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.8.7",
12
+ "version": "0.10.0",
13
13
  "license": "Apache-2.0",
14
14
  "author": "@SAP/ux-tools-team",
15
15
  "main": "dist/index.js",
@@ -25,7 +25,7 @@
25
25
  "ejs": "3.1.9",
26
26
  "@sap-ux/logger": "0.4.0",
27
27
  "@sap-ux/adp-tooling": "0.5.3",
28
- "@sap-ux/control-property-editor-sources": "npm:@sap-ux/control-property-editor@0.2.4"
28
+ "@sap-ux/control-property-editor-sources": "npm:@sap-ux/control-property-editor@0.4.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/ejs": "3.1.2",
@@ -36,7 +36,7 @@
36
36
  "npm-run-all": "4.1.5",
37
37
  "nock": "13.2.1",
38
38
  "supertest": "6.3.3",
39
- "@private/preview-middleware-client": "npm:@sap-ux-private/preview-middleware-client@0.5.1",
39
+ "@private/preview-middleware-client": "npm:@sap-ux-private/preview-middleware-client@0.7.0",
40
40
  "@sap-ux/project-access": "1.14.0",
41
41
  "@sap-ux/store": "0.4.0"
42
42
  },