@sap-ux/fe-fpm-writer 0.42.20 → 0.43.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.
- package/dist/building-block/index.js +1 -1
- package/dist/building-block/processor.d.ts +6 -1
- package/dist/building-block/processor.js +76 -37
- package/dist/building-block/prompts/utils/questions.js +2 -1
- package/dist/building-block/prompts/utils/xml.d.ts +0 -9
- package/dist/building-block/prompts/utils/xml.js +0 -14
- package/dist/building-block/types.d.ts +52 -1
- package/dist/building-block/types.js +1 -0
- package/dist/common/file.d.ts +2 -8
- package/dist/common/file.js +2 -2
- package/dist/common/utils.d.ts +9 -0
- package/dist/common/utils.js +15 -0
- package/dist/index.d.ts +1 -1
- package/dist/page/custom.js +1 -1
- package/package.json +2 -2
- package/templates/building-block/action/View.xml +30 -0
|
@@ -82,7 +82,7 @@ async function generateBuildingBlock(basePath, config, fs) {
|
|
|
82
82
|
const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fs);
|
|
83
83
|
// Read the view xml and template files and update contents of the view xml file
|
|
84
84
|
const xmlDocument = getUI5XmlDocument(basePath, viewOrFragmentPath, fs);
|
|
85
|
-
const { updatedAggregationPath, processedBuildingBlockData, hasAggregation, aggregationNamespace } = (0, processor_1.processBuildingBlock)(buildingBlockData, xmlDocument, manifestPath, manifest, aggregationPath, fs);
|
|
85
|
+
const { updatedAggregationPath, processedBuildingBlockData, hasAggregation, aggregationNamespace } = (0, processor_1.processBuildingBlock)({ ...buildingBlockData, generateId: fnGenerateId }, xmlDocument, manifestPath, manifest, aggregationPath, fs);
|
|
86
86
|
const templateConfig = {
|
|
87
87
|
hasAggregation,
|
|
88
88
|
aggregationNamespace
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { Editor } from 'mem-fs-editor';
|
|
2
|
-
import { BuildingBlockType, type BuildingBlock, type EmbededFragment } from './types';
|
|
2
|
+
import { BuildingBlockType, type BuildingBlock, type EmbededFragment, type EmbeddedAction } from './types';
|
|
3
3
|
import type { Manifest, InternalCustomElement } from '../common/types';
|
|
4
4
|
/**
|
|
5
5
|
* Type for embedded fragment data used in building block processing.
|
|
6
6
|
*/
|
|
7
7
|
type EmbeddedFragmentData = InternalCustomElement & EmbededFragment;
|
|
8
|
+
/**
|
|
9
|
+
* Type for embedded action data used in building block processing for custom actions.
|
|
10
|
+
*/
|
|
11
|
+
type EmbeddedActionData = InternalCustomElement & EmbeddedAction;
|
|
8
12
|
/**
|
|
9
13
|
* Namespace for XML elements.
|
|
10
14
|
*/
|
|
@@ -22,6 +26,7 @@ interface ProcessingContext {
|
|
|
22
26
|
embeddedFragment?: EmbeddedFragmentData;
|
|
23
27
|
updatedAggregationPath?: string;
|
|
24
28
|
hasAggregation?: boolean;
|
|
29
|
+
embeddedAction?: EmbeddedActionData;
|
|
25
30
|
}
|
|
26
31
|
/**
|
|
27
32
|
* Configuration for building block templates.
|
|
@@ -78,6 +78,11 @@ exports.BUILDING_BLOCK_CONFIG = {
|
|
|
78
78
|
aggregationConfig: { aggregationName: 'buttonGroups', elementName: 'ButtonGroup' },
|
|
79
79
|
namespace: { uri: 'sap.fe.macros', prefix: 'macros' },
|
|
80
80
|
processor: processRichTextEditorButtonGroups
|
|
81
|
+
},
|
|
82
|
+
[types_1.BuildingBlockType.Action]: {
|
|
83
|
+
aggregationConfig: { aggregationName: 'actions', elementName: 'Action' },
|
|
84
|
+
namespace: { uri: 'sap.fe.macros.table', prefix: 'macrosTable' },
|
|
85
|
+
processor: processAction
|
|
81
86
|
}
|
|
82
87
|
};
|
|
83
88
|
/**
|
|
@@ -95,13 +100,14 @@ function getBuildingBlockConfig(buildingBlockType) {
|
|
|
95
100
|
return config;
|
|
96
101
|
}
|
|
97
102
|
/**
|
|
98
|
-
*
|
|
103
|
+
* Checks if the building block data matches a specific type.
|
|
99
104
|
*
|
|
100
105
|
* @param {BuildingBlock} data - The building block data to check
|
|
101
|
-
* @
|
|
106
|
+
* @param {BuildingBlockType} type - The building block type to check against
|
|
107
|
+
* @returns {boolean} True if the data matches the specified type
|
|
102
108
|
*/
|
|
103
|
-
function
|
|
104
|
-
return data.buildingBlockType ===
|
|
109
|
+
function isBuildingBlockType(data, type) {
|
|
110
|
+
return data.buildingBlockType === type;
|
|
105
111
|
}
|
|
106
112
|
/**
|
|
107
113
|
* Processes custom column building block.
|
|
@@ -111,7 +117,7 @@ function isCustomColumn(data) {
|
|
|
111
117
|
*/
|
|
112
118
|
function processCustomColumn(buildingBlockData, context) {
|
|
113
119
|
const { fs, viewPath } = context;
|
|
114
|
-
if (!
|
|
120
|
+
if (!isBuildingBlockType(buildingBlockData, types_1.BuildingBlockType.CustomColumn)) {
|
|
115
121
|
throw new Error('Expected CustomColumn building block data');
|
|
116
122
|
}
|
|
117
123
|
const config = getBuildingBlockConfig(types_1.BuildingBlockType.CustomColumn);
|
|
@@ -130,15 +136,6 @@ function processCustomColumn(buildingBlockData, context) {
|
|
|
130
136
|
fs.copyTpl((0, templates_1.getTemplatePath)(config.templateFile), viewPath, columnConfig);
|
|
131
137
|
}
|
|
132
138
|
}
|
|
133
|
-
/**
|
|
134
|
-
* Type guard to check if the building block data is a custom filter field.
|
|
135
|
-
*
|
|
136
|
-
* @param {BuildingBlock} data - The building block data to check
|
|
137
|
-
* @returns {boolean} True if the data is a custom filter field
|
|
138
|
-
*/
|
|
139
|
-
function isCustomFilterField(data) {
|
|
140
|
-
return data.buildingBlockType === types_1.BuildingBlockType.CustomFilterField;
|
|
141
|
-
}
|
|
142
139
|
/**
|
|
143
140
|
* Processes custom filter field building block.
|
|
144
141
|
*
|
|
@@ -147,7 +144,7 @@ function isCustomFilterField(data) {
|
|
|
147
144
|
*/
|
|
148
145
|
function processCustomFilterField(buildingBlockData, context) {
|
|
149
146
|
const { fs, viewPath, embeddedFragment } = context;
|
|
150
|
-
if (!
|
|
147
|
+
if (!isBuildingBlockType(buildingBlockData, types_1.BuildingBlockType.CustomFilterField)) {
|
|
151
148
|
throw new Error('Expected CustomFilterField building block data');
|
|
152
149
|
}
|
|
153
150
|
if (!embeddedFragment) {
|
|
@@ -173,9 +170,9 @@ function processCustomFilterField(buildingBlockData, context) {
|
|
|
173
170
|
});
|
|
174
171
|
}
|
|
175
172
|
const configKey = config.templateFile;
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
178
|
-
const additionalContext =
|
|
173
|
+
const additionalDataConfig = file_1.CONFIG[configKey];
|
|
174
|
+
if (additionalDataConfig?.getData) {
|
|
175
|
+
const additionalContext = additionalDataConfig.getData(buildingBlockData.generateId);
|
|
179
176
|
filterConfig = { ...filterConfig, ...additionalContext };
|
|
180
177
|
}
|
|
181
178
|
if (viewPath && !fs.exists(viewPath)) {
|
|
@@ -281,22 +278,6 @@ function mergeButtonGroups(existingButtonGroupsMap, rteButtonGroups) {
|
|
|
281
278
|
};
|
|
282
279
|
});
|
|
283
280
|
}
|
|
284
|
-
/**
|
|
285
|
-
* Type guard to check if the building block data is a rich text editor button groups.
|
|
286
|
-
*
|
|
287
|
-
* @param {BuildingBlock} data - The building block data to check
|
|
288
|
-
* @returns {boolean} True if the data is a rich text editor button groups
|
|
289
|
-
*/
|
|
290
|
-
function isRichTextEditorButtonGroups(data) {
|
|
291
|
-
return data.buildingBlockType === types_1.BuildingBlockType.RichTextEditorButtonGroups;
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
*
|
|
295
|
-
* @param data
|
|
296
|
-
*/
|
|
297
|
-
function isRichTextEditor(data) {
|
|
298
|
-
return data.buildingBlockType === types_1.BuildingBlockType.RichTextEditor;
|
|
299
|
-
}
|
|
300
281
|
/**
|
|
301
282
|
* Processes rich text editor button groups building block.
|
|
302
283
|
*
|
|
@@ -305,7 +286,8 @@ function isRichTextEditor(data) {
|
|
|
305
286
|
*/
|
|
306
287
|
function processRichTextEditorButtonGroups(buildingBlockData, context) {
|
|
307
288
|
const { xmlDocument, updatedAggregationPath, hasAggregation } = context;
|
|
308
|
-
if (!
|
|
289
|
+
if (!isBuildingBlockType(buildingBlockData, types_1.BuildingBlockType.RichTextEditorButtonGroups) &&
|
|
290
|
+
!isBuildingBlockType(buildingBlockData, types_1.BuildingBlockType.RichTextEditor)) {
|
|
309
291
|
throw new Error('Expected RichTextEditorButtonGroups or RichTextEditor building block data');
|
|
310
292
|
}
|
|
311
293
|
const existingButtonGroupsMap = new Map();
|
|
@@ -369,6 +351,44 @@ function updateAggregationPath(xmlDocument, aggregationPath, config, namespace)
|
|
|
369
351
|
}
|
|
370
352
|
return { updatedAggregationPath: aggregationPath, hasElement: false };
|
|
371
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Processes custom action building blocks.
|
|
356
|
+
*
|
|
357
|
+
* @param buildingBlockData - The building block data
|
|
358
|
+
* @param context - Processing context
|
|
359
|
+
*/
|
|
360
|
+
function processAction(buildingBlockData, context) {
|
|
361
|
+
const { fs } = context;
|
|
362
|
+
if (!isBuildingBlockType(buildingBlockData, types_1.BuildingBlockType.Action)) {
|
|
363
|
+
throw new Error('Expected Action building block data');
|
|
364
|
+
}
|
|
365
|
+
const actionConfig = buildingBlockData.embeddedAction;
|
|
366
|
+
if (typeof actionConfig.eventHandler === 'object') {
|
|
367
|
+
const processedEventHandler = (0, event_handler_1.applyEventHandlerConfiguration)(fs, actionConfig, actionConfig.eventHandler, {
|
|
368
|
+
typescript: actionConfig.typescript
|
|
369
|
+
});
|
|
370
|
+
const fnName = actionConfig.eventHandler ? actionConfig.eventHandler.fnName : processedEventHandler;
|
|
371
|
+
// Check if file name includes .controller
|
|
372
|
+
if (actionConfig.eventHandler.fileName?.includes('.controller')) {
|
|
373
|
+
// Controller method: use fnName as is, no core:require needed
|
|
374
|
+
actionConfig.eventHandler = {
|
|
375
|
+
fnName: `.${fnName}`
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
// Custom handler file: use handler alias with core:require
|
|
380
|
+
let handlerPath;
|
|
381
|
+
if (actionConfig.eventHandler.fileName) {
|
|
382
|
+
const path = context.embeddedAction?.ns?.split('.').join('/');
|
|
383
|
+
handlerPath = node_path_1.posix.join(path ?? '', actionConfig.eventHandler.fileName);
|
|
384
|
+
}
|
|
385
|
+
actionConfig.eventHandler = {
|
|
386
|
+
fnName: `handler.${fnName}`,
|
|
387
|
+
fileName: handlerPath
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
372
392
|
/**
|
|
373
393
|
* Processes building block configuration.
|
|
374
394
|
*
|
|
@@ -397,7 +417,8 @@ function processBuildingBlock(buildingBlockData, xmlDocument, manifestPath, mani
|
|
|
397
417
|
aggregationNamespace
|
|
398
418
|
};
|
|
399
419
|
}
|
|
400
|
-
if (
|
|
420
|
+
if (isBuildingBlockType(buildingBlockData, types_1.BuildingBlockType.RichTextEditorButtonGroups) ||
|
|
421
|
+
isBuildingBlockType(buildingBlockData, types_1.BuildingBlockType.RichTextEditor)) {
|
|
401
422
|
const result = updateAggregationPath(xmlDocument, aggregationPath, {
|
|
402
423
|
aggregationName: config.aggregationConfig.aggregationName,
|
|
403
424
|
elementName: config.aggregationConfig.elementName
|
|
@@ -415,7 +436,8 @@ function processBuildingBlock(buildingBlockData, xmlDocument, manifestPath, mani
|
|
|
415
436
|
aggregationNamespace = (0, xml_1.getOrAddNamespace)(xmlDocument, config.namespace.uri, config.namespace.prefix);
|
|
416
437
|
}
|
|
417
438
|
// Process embedded fragment for types that support it
|
|
418
|
-
if ((
|
|
439
|
+
if ((isBuildingBlockType(buildingBlockData, types_1.BuildingBlockType.CustomColumn) ||
|
|
440
|
+
isBuildingBlockType(buildingBlockData, types_1.BuildingBlockType.CustomFilterField)) &&
|
|
419
441
|
buildingBlockData.embededFragment) {
|
|
420
442
|
embeddedFragment = (0, defaults_1.setCommonDefaults)(buildingBlockData.embededFragment, manifestPath, manifest);
|
|
421
443
|
viewPath = (0, node_path_1.join)(embeddedFragment.path, `${embeddedFragment.fragmentFile ?? embeddedFragment.name}.fragment.xml`);
|
|
@@ -434,6 +456,23 @@ function processBuildingBlock(buildingBlockData, xmlDocument, manifestPath, mani
|
|
|
434
456
|
hasAggregation = result.hasElement;
|
|
435
457
|
aggregationNamespace = (0, xml_1.getOrAddNamespace)(xmlDocument, config.namespace.uri, config.namespace.prefix);
|
|
436
458
|
}
|
|
459
|
+
if (isBuildingBlockType(buildingBlockData, types_1.BuildingBlockType.Action) && buildingBlockData.embeddedAction) {
|
|
460
|
+
const result = updateAggregationPath(xmlDocument, aggregationPath, {
|
|
461
|
+
aggregationName: config.aggregationConfig.aggregationName,
|
|
462
|
+
elementName: config.aggregationConfig.elementName
|
|
463
|
+
});
|
|
464
|
+
const context = {
|
|
465
|
+
fs,
|
|
466
|
+
xmlDocument,
|
|
467
|
+
updatedAggregationPath: result.updatedAggregationPath,
|
|
468
|
+
hasAggregation: result.hasElement,
|
|
469
|
+
embeddedAction: (0, defaults_1.setCommonDefaults)(buildingBlockData.embeddedAction, manifestPath, manifest)
|
|
470
|
+
};
|
|
471
|
+
config.processor(buildingBlockData, context);
|
|
472
|
+
updatedAggregationPath = result.updatedAggregationPath;
|
|
473
|
+
hasAggregation = result.hasElement;
|
|
474
|
+
(0, xml_1.getOrAddNamespace)(xmlDocument, config.namespace.uri, config.namespace.prefix);
|
|
475
|
+
}
|
|
437
476
|
return {
|
|
438
477
|
updatedAggregationPath,
|
|
439
478
|
processedBuildingBlockData: buildingBlockData,
|
|
@@ -20,6 +20,7 @@ const xml_1 = require("./xml");
|
|
|
20
20
|
const i18n_1 = require("../../../i18n");
|
|
21
21
|
const prompt_helpers_1 = require("./prompt-helpers");
|
|
22
22
|
const file_1 = require("../../../common/file");
|
|
23
|
+
const utils_1 = require("../../../common/utils");
|
|
23
24
|
/* eslint-disable @typescript-eslint/no-floating-promises */
|
|
24
25
|
(0, i18n_1.initI18n)();
|
|
25
26
|
const t = (0, i18n_1.translate)(i18n_1.i18nNamespaces.buildingBlock, 'prompts.common.');
|
|
@@ -374,7 +375,7 @@ function getBuildingBlockIdPrompt(context, validationErrorMessage, properties =
|
|
|
374
375
|
}
|
|
375
376
|
else {
|
|
376
377
|
return answers?.viewOrFragmentPath &&
|
|
377
|
-
!(0,
|
|
378
|
+
!(0, utils_1.isElementIdAvailable)(fs, (0, node_path_1.join)(appPath, answers.viewOrFragmentPath), value)
|
|
378
379
|
? t('id.existingIdValidation')
|
|
379
380
|
: true;
|
|
380
381
|
}
|
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
import type { Editor } from 'mem-fs-editor';
|
|
2
|
-
/**
|
|
3
|
-
* Method validates if passed id is available.
|
|
4
|
-
*
|
|
5
|
-
* @param fs - the file system object for reading files
|
|
6
|
-
* @param viewOrFragmentPath - path to fragment or view file
|
|
7
|
-
* @param id - id to check/validate
|
|
8
|
-
* @returns true if passed id is available.
|
|
9
|
-
*/
|
|
10
|
-
export declare function isElementIdAvailable(fs: Editor, viewOrFragmentPath: string, id: string): boolean;
|
|
11
2
|
/**
|
|
12
3
|
* Converts the provided xpath string from `/mvc:View/Page/content` to
|
|
13
4
|
* `/mvc:View/*[local-name()='Page']/*[local-name()='content']`.
|
|
@@ -34,26 +34,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.augmentXpathWithLocalNames = void 0;
|
|
37
|
-
exports.isElementIdAvailable = isElementIdAvailable;
|
|
38
37
|
exports.getXPathStringsForXmlFile = getXPathStringsForXmlFile;
|
|
39
38
|
exports.getFilterBarIdsInFile = getFilterBarIdsInFile;
|
|
40
39
|
exports.getExistingButtonGroups = getExistingButtonGroups;
|
|
41
40
|
exports.getOrAddNamespace = getOrAddNamespace;
|
|
42
41
|
const xmldom_1 = require("@xmldom/xmldom");
|
|
43
42
|
const xpath = __importStar(require("xpath"));
|
|
44
|
-
/**
|
|
45
|
-
* Method validates if passed id is available.
|
|
46
|
-
*
|
|
47
|
-
* @param fs - the file system object for reading files
|
|
48
|
-
* @param viewOrFragmentPath - path to fragment or view file
|
|
49
|
-
* @param id - id to check/validate
|
|
50
|
-
* @returns true if passed id is available.
|
|
51
|
-
*/
|
|
52
|
-
function isElementIdAvailable(fs, viewOrFragmentPath, id) {
|
|
53
|
-
const xmlContent = fs.read(viewOrFragmentPath).toString();
|
|
54
|
-
const xmlDocument = new xmldom_1.DOMParser({ errorHandler: () => { } }).parseFromString(xmlContent);
|
|
55
|
-
return xmlDocument.documentElement ? !xmlDocument.getElementById(id) : true;
|
|
56
|
-
}
|
|
57
43
|
/**
|
|
58
44
|
* Converts the provided xpath string from `/mvc:View/Page/content` to
|
|
59
45
|
* `/mvc:View/*[local-name()='Page']/*[local-name()='content']`.
|
|
@@ -15,7 +15,8 @@ export declare enum BuildingBlockType {
|
|
|
15
15
|
Table = "table",
|
|
16
16
|
CustomColumn = "custom-column",
|
|
17
17
|
RichTextEditor = "rich-text-editor",
|
|
18
|
-
RichTextEditorButtonGroups = "rich-text-editor-button-groups"
|
|
18
|
+
RichTextEditorButtonGroups = "rich-text-editor-button-groups",
|
|
19
|
+
Action = "action"
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
21
22
|
* Binding context type.
|
|
@@ -425,6 +426,56 @@ export interface CustomColumn extends BuildingBlock {
|
|
|
425
426
|
position?: Position;
|
|
426
427
|
embededFragment?: EmbededFragment;
|
|
427
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Building block for adding custom actions to tables.
|
|
431
|
+
* Custom actions can be added to table toolbars and can trigger controller methods or fragments.
|
|
432
|
+
*
|
|
433
|
+
* @see https://sapui5.hana.ondemand.com/#/api/sap.fe.macros.table.Action
|
|
434
|
+
* @example
|
|
435
|
+
* // Simple action with event handler
|
|
436
|
+
* <macros:Table id="MyTable" metaPath="@com.sap.vocabularies.UI.v1.LineItem">
|
|
437
|
+
* <macros:actions>
|
|
438
|
+
* <macrosTable:Action
|
|
439
|
+
* key="approveAction"
|
|
440
|
+
* text="Approve"
|
|
441
|
+
* press=".onApprove"
|
|
442
|
+
* requiresSelection="true"
|
|
443
|
+
* placement="After"
|
|
444
|
+
* />
|
|
445
|
+
* </macros:actions>
|
|
446
|
+
* </macros:Table>
|
|
447
|
+
* @extends {BuildingBlock}
|
|
448
|
+
*/
|
|
449
|
+
export interface Action extends BuildingBlock {
|
|
450
|
+
/**
|
|
451
|
+
* Unique identifier of the action.
|
|
452
|
+
*/
|
|
453
|
+
actionKey: string;
|
|
454
|
+
/**
|
|
455
|
+
* The text that will be displayed for this action.
|
|
456
|
+
*/
|
|
457
|
+
text: string;
|
|
458
|
+
/**
|
|
459
|
+
* Reference to the key of another action already displayed in the toolbar to properly place this one.
|
|
460
|
+
*/
|
|
461
|
+
anchor?: string;
|
|
462
|
+
/**
|
|
463
|
+
* Defines where this action should be placed relative to the defined anchor.
|
|
464
|
+
* Allowed values are 'Before' and 'After'.
|
|
465
|
+
*/
|
|
466
|
+
placement?: 'Before' | 'After';
|
|
467
|
+
/**
|
|
468
|
+
* Defines if the action requires a selection.
|
|
469
|
+
*
|
|
470
|
+
* @default false
|
|
471
|
+
*/
|
|
472
|
+
requiresSelection?: boolean;
|
|
473
|
+
/**
|
|
474
|
+
* This allows you to define event handlers, or custom XML elements for the action.
|
|
475
|
+
*/
|
|
476
|
+
embeddedAction: EmbeddedAction;
|
|
477
|
+
}
|
|
478
|
+
export type EmbeddedAction = EventHandler & CustomFragment & CustomElement;
|
|
428
479
|
export type EmbededFragment = EventHandler & CustomFragment & CustomElement & FragmentContentData;
|
|
429
480
|
/**
|
|
430
481
|
* Building block used to create a rich text editor based on the metadata provided by OData V4.
|
|
@@ -18,6 +18,7 @@ var BuildingBlockType;
|
|
|
18
18
|
BuildingBlockType["CustomColumn"] = "custom-column";
|
|
19
19
|
BuildingBlockType["RichTextEditor"] = "rich-text-editor";
|
|
20
20
|
BuildingBlockType["RichTextEditorButtonGroups"] = "rich-text-editor-button-groups";
|
|
21
|
+
BuildingBlockType["Action"] = "action";
|
|
21
22
|
})(BuildingBlockType || (exports.BuildingBlockType = BuildingBlockType = {}));
|
|
22
23
|
exports.bindingContextAbsolute = 'absolute';
|
|
23
24
|
exports.bindingContextRelative = 'relative';
|
package/dist/common/file.d.ts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import type { CopyOptions, Editor } from 'mem-fs-editor';
|
|
2
2
|
import type { TabInfo } from '../common/types';
|
|
3
3
|
interface ButtonGroup {
|
|
4
|
-
name: string;
|
|
5
|
-
buttons: string[];
|
|
6
|
-
visible?: boolean;
|
|
7
|
-
priority?: number;
|
|
8
|
-
customToolbarPriority?: number;
|
|
9
|
-
row?: number;
|
|
10
4
|
id?: string;
|
|
11
5
|
}
|
|
12
6
|
export type IdGeneratorFunction = (baseId: string, validatedIds?: string[]) => string;
|
|
13
|
-
interface TemplateContext {
|
|
7
|
+
export interface TemplateContext {
|
|
14
8
|
buttonGroups?: ButtonGroup[];
|
|
15
9
|
name?: string;
|
|
16
10
|
data?: {
|
|
@@ -49,7 +43,7 @@ export declare const CONFIG: {
|
|
|
49
43
|
};
|
|
50
44
|
};
|
|
51
45
|
"building-block/rich-text-editor-button-groups/View.xml": {
|
|
52
|
-
getData: (generateId: IdGeneratorFunction, context?: TemplateContext) => {
|
|
46
|
+
getData: (generateId: IdGeneratorFunction, context?: Partial<TemplateContext>) => {
|
|
53
47
|
ids: Record<string, string>;
|
|
54
48
|
};
|
|
55
49
|
};
|
package/dist/common/file.js
CHANGED
|
@@ -10,7 +10,7 @@ exports.copyTpl = copyTpl;
|
|
|
10
10
|
exports.getRelativeTemplateComponentPath = getRelativeTemplateComponentPath;
|
|
11
11
|
const node_path_1 = require("node:path");
|
|
12
12
|
const file_1 = require("@sap-ux/project-access/dist/file");
|
|
13
|
-
const utils_1 = require("
|
|
13
|
+
const utils_1 = require("./utils");
|
|
14
14
|
const CHAR_SPACE = ' ';
|
|
15
15
|
const CHAR_TAB = '\t';
|
|
16
16
|
exports.CONFIG = {
|
|
@@ -82,7 +82,7 @@ exports.CONFIG = {
|
|
|
82
82
|
const ids = {};
|
|
83
83
|
const validatedIds = [];
|
|
84
84
|
buttonGroups.forEach((group, index) => {
|
|
85
|
-
const id = generateId(
|
|
85
|
+
const id = generateId('ButtonGroup', validatedIds);
|
|
86
86
|
ids[index] = group.id ?? id;
|
|
87
87
|
if (!group.id) {
|
|
88
88
|
validatedIds.push(id);
|
package/dist/common/utils.d.ts
CHANGED
|
@@ -48,4 +48,13 @@ export declare function getManifestPath(basePath: string, fs: Editor): Promise<s
|
|
|
48
48
|
* @returns {Manifest | undefined} The content and path of the manifest
|
|
49
49
|
*/
|
|
50
50
|
export declare function getManifest(basePath: string, fs: Editor, validate?: boolean): Promise<ManifestData>;
|
|
51
|
+
/**
|
|
52
|
+
* Method validates if passed id is available.
|
|
53
|
+
*
|
|
54
|
+
* @param fs - the file system object for reading files
|
|
55
|
+
* @param viewOrFragmentPath - path to fragment or view file
|
|
56
|
+
* @param id - id to check/validate
|
|
57
|
+
* @returns true if passed id is available.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isElementIdAvailable(fs: Editor, viewOrFragmentPath: string, id: string): boolean;
|
|
51
60
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/common/utils.js
CHANGED
|
@@ -8,12 +8,14 @@ exports.insertTextAtPosition = insertTextAtPosition;
|
|
|
8
8
|
exports.addExtensionTypes = addExtensionTypes;
|
|
9
9
|
exports.getManifestPath = getManifestPath;
|
|
10
10
|
exports.getManifest = getManifest;
|
|
11
|
+
exports.isElementIdAvailable = isElementIdAvailable;
|
|
11
12
|
const node_os_1 = __importDefault(require("node:os"));
|
|
12
13
|
const node_path_1 = require("node:path");
|
|
13
14
|
const semver_1 = require("semver");
|
|
14
15
|
const project_access_1 = require("@sap-ux/project-access");
|
|
15
16
|
const templates_1 = require("../templates");
|
|
16
17
|
const file_1 = require("./file");
|
|
18
|
+
const xmldom_1 = require("@xmldom/xmldom");
|
|
17
19
|
/**
|
|
18
20
|
* Method inserts passed text into content by char index position.
|
|
19
21
|
* In case if position is out of range, then whitespaces would be created.
|
|
@@ -103,4 +105,17 @@ async function getManifest(basePath, fs, validate = true) {
|
|
|
103
105
|
content: fs.readJSON(path)
|
|
104
106
|
};
|
|
105
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Method validates if passed id is available.
|
|
110
|
+
*
|
|
111
|
+
* @param fs - the file system object for reading files
|
|
112
|
+
* @param viewOrFragmentPath - path to fragment or view file
|
|
113
|
+
* @param id - id to check/validate
|
|
114
|
+
* @returns true if passed id is available.
|
|
115
|
+
*/
|
|
116
|
+
function isElementIdAvailable(fs, viewOrFragmentPath, id) {
|
|
117
|
+
const xmlContent = fs.read(viewOrFragmentPath).toString();
|
|
118
|
+
const xmlDocument = new xmldom_1.DOMParser({ errorHandler: () => { } }).parseFromString(xmlContent);
|
|
119
|
+
return xmlDocument.documentElement ? !xmlDocument.getElementById(id) : true;
|
|
120
|
+
}
|
|
106
121
|
//# sourceMappingURL=utils.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { CustomView } from './view/types';
|
|
|
14
14
|
export { generateCustomView } from './view';
|
|
15
15
|
export { enableFPM, FPMConfig } from './app';
|
|
16
16
|
export { validateBasePath, validateVersion } from './common/validate';
|
|
17
|
-
export { BuildingBlockType, FilterBar, Form, Chart, Field, FieldFormatOptions, Table, BuildingBlockConfig, Page, CustomColumn, CustomFilterField, RichTextEditor, ButtonGroupConfig } from './building-block/types';
|
|
17
|
+
export { BuildingBlockType, FilterBar, Form, Chart, Field, FieldFormatOptions, Table, BuildingBlockConfig, Page, CustomColumn, CustomFilterField, RichTextEditor, ButtonGroupConfig, Action } from './building-block/types';
|
|
18
18
|
export { generateBuildingBlock, getSerializedFileContent } from './building-block';
|
|
19
19
|
export { ChartPromptsAnswer, FilterBarPromptsAnswer, FormPromptsAnswer, TablePromptsAnswer, PagePromptsAnswer, RichTextEditorPromptsAnswer, RichTextEditorButtonGroupsPromptsAnswer, BuildingBlockTypePromptsAnswer } from './building-block/prompts/questions';
|
|
20
20
|
export { PromptsType, SupportedGeneratorAnswers, PromptsAPI, PromptsGroup, Prompts, ValidationResults, Answers, Subset, CodeSnippet } from './prompts';
|
package/dist/page/custom.js
CHANGED
|
@@ -91,7 +91,7 @@ async function handlePageBuildingBlock(basePath, data, viewPath, fs, generateId,
|
|
|
91
91
|
log?.warn(t('minUi5VersionRequirement', { minUI5Version: data.minUI5Version }));
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
|
-
const pageId =
|
|
94
|
+
const pageId = generateId('Page');
|
|
95
95
|
await (0, building_block_1.generateBuildingBlock)(basePath, {
|
|
96
96
|
viewOrFragmentPath: (0, node_path_1.relative)(basePath, viewPath),
|
|
97
97
|
aggregationPath: (0, utils_2.augmentXpathWithLocalNames)(`/mvc:View/Page`),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap-ux/fe-fpm-writer",
|
|
3
3
|
"description": "SAP Fiori elements flexible programming model writer",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.43.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/SAP/open-ux-tools.git",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"semver": "7.7.4",
|
|
31
31
|
"xml-formatter": "2.6.1",
|
|
32
32
|
"xpath": "0.0.33",
|
|
33
|
-
"@sap-ux/fiori-annotation-api": "0.9.
|
|
33
|
+
"@sap-ux/fiori-annotation-api": "0.9.25",
|
|
34
34
|
"@sap-ux/project-access": "1.35.10",
|
|
35
35
|
"@sap-ux/logger": "0.8.1"
|
|
36
36
|
},
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<%_ if (!config?.hasAggregation) { _%>
|
|
2
|
+
<<%- macrosNamespace %>:actions>
|
|
3
|
+
<%_ } _%>
|
|
4
|
+
<<%- config?.aggregationNamespace %>:Action
|
|
5
|
+
<%_ if (data.embeddedAction?.eventHandler?.fileName) { _%>
|
|
6
|
+
core:require="{ handler: '<%- data.embeddedAction.eventHandler.fileName %>' }"
|
|
7
|
+
<%_ } _%>
|
|
8
|
+
<%_ if (data.id) { _%>
|
|
9
|
+
id="<%- data.id %>"
|
|
10
|
+
<%_ } _%>
|
|
11
|
+
<%_ if (data.actionKey) { _%>
|
|
12
|
+
key="<%- data.actionKey %>"
|
|
13
|
+
<%_ } _%>
|
|
14
|
+
text="<%- data.text %>"
|
|
15
|
+
<%_ if (data.embeddedAction?.eventHandler?.fnName) { _%>
|
|
16
|
+
press="<%- data.embeddedAction.eventHandler.fnName %>"
|
|
17
|
+
<%_ } _%>
|
|
18
|
+
<%_ if (data.anchor) { _%>
|
|
19
|
+
anchor="<%- data.anchor %>"
|
|
20
|
+
<%_ } _%>
|
|
21
|
+
<%_ if (data.placement) { _%>
|
|
22
|
+
placement="<%- data.placement %>"
|
|
23
|
+
<%_ } _%>
|
|
24
|
+
<%_ if (data.requiresSelection !== undefined) { _%>
|
|
25
|
+
requiresSelection="<%- data.requiresSelection %>"
|
|
26
|
+
<%_ } _%>
|
|
27
|
+
/>
|
|
28
|
+
<%_ if (!config?.hasAggregation) { _%>
|
|
29
|
+
</<%- macrosNamespace %>:actions>
|
|
30
|
+
<%_ } _%>
|