@siliconst/metaui 1.0.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.
Files changed (25) hide show
  1. package/README.md +123 -0
  2. package/package.json +35 -0
  3. package/src/nz/co/siliconst/ui5/metaui/.library +7 -0
  4. package/src/nz/co/siliconst/ui5/metaui/controls/GeneratorHost.ts +162 -0
  5. package/src/nz/co/siliconst/ui5/metaui/core/ConditionEngine.ts +40 -0
  6. package/src/nz/co/siliconst/ui5/metaui/core/Engine.ts +56 -0
  7. package/src/nz/co/siliconst/ui5/metaui/core/EventBus.ts +45 -0
  8. package/src/nz/co/siliconst/ui5/metaui/core/PluginRegistry.ts +56 -0
  9. package/src/nz/co/siliconst/ui5/metaui/core/SchemaNormalizer.ts +31 -0
  10. package/src/nz/co/siliconst/ui5/metaui/core/StateManager.ts +56 -0
  11. package/src/nz/co/siliconst/ui5/metaui/interfaces/IEventBus.ts +29 -0
  12. package/src/nz/co/siliconst/ui5/metaui/interfaces/ILayoutManager.ts +26 -0
  13. package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin.ts +33 -0
  14. package/src/nz/co/siliconst/ui5/metaui/interfaces/ISchema.ts +70 -0
  15. package/src/nz/co/siliconst/ui5/metaui/layouts/BaseLayout.ts +34 -0
  16. package/src/nz/co/siliconst/ui5/metaui/layouts/FormLayout.ts +60 -0
  17. package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout.ts +102 -0
  18. package/src/nz/co/siliconst/ui5/metaui/library.ts +43 -0
  19. package/src/nz/co/siliconst/ui5/metaui/plugins/BasePlugin.ts +58 -0
  20. package/src/nz/co/siliconst/ui5/metaui/plugins/BooleanPlugin.ts +42 -0
  21. package/src/nz/co/siliconst/ui5/metaui/plugins/DatePlugin.ts +63 -0
  22. package/src/nz/co/siliconst/ui5/metaui/plugins/DropdownPlugin.ts +65 -0
  23. package/src/nz/co/siliconst/ui5/metaui/plugins/NumberPlugin.ts +55 -0
  24. package/src/nz/co/siliconst/ui5/metaui/plugins/StringPlugin.ts +55 -0
  25. package/ui5.yaml +12 -0
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # MetaUI - Dynamic SAP UI5 Rendering Engine
2
+
3
+ MetaUI is an UI5 library that dynamically generates fully accessible, Fiori-compliant forms and tables purely from JSON schemas. It utilizes an **Isolated State Pattern** to entirely decouple from host OData bindings, yielding a clean JSON payload upon submission.
4
+
5
+ ## 🏗️ Architecture
6
+ 1. **Standard UI5**: Relies strictly on native UI5 layout grids (`sap.ui.layout.form.SimpleForm`, `sap.m.Table`).
7
+ 2. **Schema Normalization**: Ingests ABAP RTTI JSON payloads and normalizes them into a strict `ISchema` layout matrix.
8
+ 3. **Plugin Registry**: Maps primitive schema types (`string`, `number`, `boolean`, `dropdown`, `date`) directly to discrete UI5 control plugins (`sap.m.Input`, `sap.m.StepInput`, etc.).
9
+
10
+ ## 📚 Documentation Wiki
11
+ Full payload references and API details are tracked in Git under the `docs/` folder:
12
+ * [Payload Formats & Schema API](docs/payload_formats.md)
13
+
14
+ ## 🚀 How to Test the Sandbox
15
+
16
+ To understand how the engine works, a standalone test sandbox has been provided. It asynchronously loads two physical files:
17
+ * `test/mockSchema.json` - The schema definition detailing field types, required states, and dropdown arrays.
18
+ * `test/mockData.json` - The initial state payload to populate the form.
19
+
20
+ ### Running the Test
21
+ You **cannot** run this test by double-clicking the `index.html` file due to browser CORS restrictions blocking `fetch()`. You must run it via a local web server.
22
+
23
+ 1. Open a terminal in the root `MetaUI` project folder.
24
+ 2. Run a static file server using Node.js:
25
+ ```bash
26
+ npx serve -p 8080 .
27
+ ```
28
+ 3. Open your web browser and navigate to:
29
+ `http://localhost:8080/test/index.html`
30
+ 4. Click **"Open MetaUI Dialog"**.
31
+ 5. Modify the values in the form. Notice the built-in validation rules (e.g., required fields, dropdown selections).
32
+ 6. Click **Save**. The clean, extracted JSON payload will be printed directly to the text area.
33
+
34
+ ## 📦 Installation (NPM)
35
+
36
+ If you are developing a modern UI5 application utilizing `ui5-tooling`, you can pull this engine in directly via NPM (once published to your internal registry):
37
+
38
+ ```bash
39
+ npm install @siliconst/ui5-metaui
40
+ ```
41
+
42
+ ### 1. ui5.yaml Configuration
43
+ Ensure your application consumes the library by adding it to your `ui5.yaml` dependencies:
44
+
45
+ ```yaml
46
+ specVersion: "3.0"
47
+ metadata:
48
+ name: my.custom.app
49
+ type: application
50
+ builder:
51
+ customTasks:
52
+ - name: ui5-tooling-transpile-task
53
+ afterTask: replaceVersion
54
+ ---
55
+ specVersion: "3.0"
56
+ kind: extension
57
+ type: project-shim
58
+ metadata:
59
+ name: my.custom.app.shim
60
+ shims:
61
+ configurations:
62
+ "@siliconst/ui5-metaui":
63
+ type: library
64
+ ```
65
+
66
+ ### 2. manifest.json Registration
67
+ You must explicitly declare the library dependency in your application's `manifest.json` so the UI5 core automatically downloads the runtime files and triggers the internal `PluginRegistry` bootstrapper.
68
+
69
+ ```json
70
+ "sap.ui5": {
71
+ "dependencies": {
72
+ "minUI5Version": "1.120.0",
73
+ "libs": {
74
+ "sap.m": {},
75
+ "sap.ui.core": {},
76
+ "nz.co.siliconst.ui5.metaui": {}
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ ## 💻 Instantiating the Engine in your Controller
83
+
84
+ Once installed and registered, simply require the `GeneratorHost` control in your view controllers:
85
+
86
+ ```javascript
87
+ sap.ui.define([
88
+ "sap/ui/core/mvc/Controller",
89
+ "nz/co/siliconst/ui5/metaui/controls/GeneratorHost"
90
+ ], function(Controller, GeneratorHost) {
91
+ return Controller.extend("my.app.controller.Main", {
92
+
93
+ openDynamicForm: function(schemaPayload, dataPayload) {
94
+ const host = new GeneratorHost({
95
+ schemaDefinition: schemaPayload, // The RTTI schema from ABAP
96
+ initialData: dataPayload, // Your isolated JSON payload
97
+ submit: (oEvent) => {
98
+ const finalJsonPayload = oEvent.getParameter("payload");
99
+ // Send finalJsonPayload back to SAP ABAP via your REST/OData API!
100
+ }
101
+ });
102
+
103
+ host.openInDialog("Edit Customer");
104
+ }
105
+ });
106
+ });
107
+ ```
108
+
109
+ ## 🔗 Declarative XML Model Binding
110
+ Because `GeneratorHost` extends `sap.ui.core.Control`, you can also embed it directly into your XML Views and bind standard UI5 Models (JSON, OData) natively to its properties.
111
+
112
+ First, add the namespace to your XML View:
113
+ ```xml
114
+ xmlns:meta="nz.co.siliconst.ui5.metaui.controls"
115
+ ```
116
+
117
+ Then bind your host application's models directly to the control:
118
+ ```xml
119
+ <meta:GeneratorHost
120
+ schemaDefinition="{schemaModel>/CustomerSchema}"
121
+ initialData="{odataModel>/Customers('1001')}" />
122
+ ```
123
+ *Note: The engine will safely clone the incoming `initialData` into its own isolated state, preventing any unwanted two-way binding bleed into your host OData models until you explicitly catch the `submit` event!*
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@siliconst/metaui",
3
+ "version": "1.0.0",
4
+ "description": "Dynamic Metadata UI5 Engine",
5
+ "author": "SiliconStreetDev1",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/SiliconStreetDev1/MetaUI.git"
10
+ },
11
+ "keywords": [
12
+ "ui5",
13
+ "metadata",
14
+ "engine"
15
+ ],
16
+ "files": [
17
+ "src",
18
+ "ui5.yaml",
19
+ "README.md"
20
+ ],
21
+ "scripts": {
22
+ "build": "ui5 build --clean-dest"
23
+ },
24
+ "devDependencies": {
25
+ "@types/openui5": "^1.120.0",
26
+ "@ui5/cli": "^3.0.0",
27
+ "typescript": "^5.3.0",
28
+ "ui5-tooling-transpile": "^3.2.0"
29
+ },
30
+ "ui5": {
31
+ "dependencies": [
32
+ "ui5-tooling-transpile"
33
+ ]
34
+ }
35
+ }
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <library xmlns="http://www.sap.com/sap.ui.library.xsd">
3
+ <name>nz.co.siliconst.ui5.metaui</name>
4
+ <vendor>Siliconst</vendor>
5
+ <version>1.0.0</version>
6
+ <title>MetaUI Engine</title>
7
+ </library>
@@ -0,0 +1,162 @@
1
+ /**
2
+ * @file GeneratorHost.ts
3
+ * @description The core UI5 custom control that exposes the MetaUI engine to host applications.
4
+ */
5
+
6
+ import Control from "sap/ui/core/Control";
7
+ import RenderManager from "sap/ui/core/RenderManager";
8
+ import Dialog from "sap/m/Dialog";
9
+ import Button from "sap/m/Button";
10
+ import { SchemaNormalizer } from "../core/SchemaNormalizer";
11
+ import { Engine } from "../core/Engine";
12
+ import { StateManager } from "../core/StateManager";
13
+ import { BaseLayout } from "../layouts/BaseLayout";
14
+
15
+ /**
16
+ * Enterprise wrapper element for embedding or popping the dynamic form.
17
+ *
18
+ * @namespace nz.co.siliconst.ui5.metaui.controls
19
+ */
20
+ export default class GeneratorHost extends Control {
21
+
22
+ private stateManager: StateManager | null = null;
23
+ private generatedContent: Control | null = null;
24
+
25
+ static readonly metadata = {
26
+ properties: {
27
+ /** The raw JSON metadata schema provided by the ABAP backend. */
28
+ schemaDefinition: { type: "object", defaultValue: null },
29
+ /** The initial data payload to populate the fields. */
30
+ initialData: { type: "object", defaultValue: {} }
31
+ },
32
+ aggregations: {
33
+ /** The internal hidden container holding the generated layouts. */
34
+ _content: { type: "sap.ui.core.Control", multiple: false, visibility: "hidden" }
35
+ },
36
+ events: {
37
+ /** Triggered when the user submits the form via the Dialog modality. */
38
+ submit: {
39
+ parameters: {
40
+ /** The clean extracted JSON payload representing the current state. */
41
+ payload: { type: "object" }
42
+ }
43
+ },
44
+ /** Triggered if the dialog is cancelled. */
45
+ cancel: {}
46
+ }
47
+ };
48
+
49
+ /**
50
+ * Standard UI5 control renderer. Renders the generated internal content.
51
+ */
52
+ static renderer = {
53
+ apiVersion: 2,
54
+ render(rm: RenderManager, control: GeneratorHost) {
55
+ rm.openStart("div", control);
56
+ rm.style("width", "100%");
57
+ rm.style("height", "100%");
58
+ rm.openEnd();
59
+
60
+ const content = control.getAggregation("_content") as Control;
61
+ if (content) {
62
+ rm.renderControl(content);
63
+ }
64
+
65
+ rm.close("div");
66
+ }
67
+ };
68
+
69
+ /**
70
+ * Initializes the UI5 control.
71
+ */
72
+ public init(): void {
73
+ super.init();
74
+ }
75
+
76
+ /**
77
+ * Lifecycle hook to automatically generate the layout if bound declaratively in XML.
78
+ */
79
+ public onBeforeRendering(): void {
80
+ if (!this.generatedContent && this.getProperty("schemaDefinition")) {
81
+ this.generate();
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Extracts the payload and fires the submit event manually. Useful for inline XML embedding.
87
+ */
88
+ public triggerSubmit(): void {
89
+ if (this.stateManager) {
90
+ const payload = this.stateManager.extractPayload();
91
+ this.fireEvent("submit", { payload });
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Triggers the engine to parse the schema and build the layout.
97
+ * Must be called manually or hooked into onBeforeRendering if properties are bound.
98
+ */
99
+ public generate(): void {
100
+ const rawSchema = this.getProperty("schemaDefinition");
101
+ const initialData = this.getProperty("initialData");
102
+
103
+ if (!rawSchema) {
104
+ throw new Error("[MetaUI] Cannot generate layout without a schemaDefinition.");
105
+ }
106
+
107
+ // 1. Normalize schema
108
+ const normalizedSchema = SchemaNormalizer.normalize(rawSchema);
109
+
110
+ // 2. Bootstrap isolated State Manager
111
+ this.stateManager = new StateManager(initialData);
112
+ this.setModel(this.stateManager.getModel(), "meta");
113
+
114
+ // 3. Delegate to Layout Engine
115
+ const engine = new Engine();
116
+ const layoutFactory = new BaseLayout();
117
+
118
+ this.generatedContent = engine.build(normalizedSchema, layoutFactory);
119
+
120
+ // 4. Mount into hidden aggregation
121
+ this.setAggregation("_content", this.generatedContent);
122
+ }
123
+
124
+ /**
125
+ * Dialog Modality: Opens the generated layout inside a sap.m.Dialog.
126
+ * Automatically wires up 'Save' and 'Cancel' buttons.
127
+ */
128
+ public openInDialog(title: string = "Dynamic Form", submitButtonText: string = "Save"): void {
129
+ if (!this.generatedContent) {
130
+ this.generate();
131
+ }
132
+
133
+ const dialog = new Dialog({
134
+ title: title,
135
+ contentWidth: "800px",
136
+ content: [this], // Mount self inside the dialog
137
+ beginButton: new Button({
138
+ text: submitButtonText,
139
+ type: "Emphasized",
140
+ press: () => {
141
+ if (this.stateManager) {
142
+ const payload = this.stateManager.extractPayload();
143
+ this.fireEvent("submit", { payload });
144
+ }
145
+ dialog.close();
146
+ }
147
+ }),
148
+ endButton: new Button({
149
+ text: "Cancel",
150
+ press: () => {
151
+ this.fireEvent("cancel");
152
+ dialog.close();
153
+ }
154
+ }),
155
+ afterClose: () => {
156
+ dialog.destroy();
157
+ }
158
+ });
159
+
160
+ dialog.open();
161
+ }
162
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @file ConditionEngine.ts
3
+ * @description Evaluates dynamic rules to alter the schema in real-time.
4
+ */
5
+
6
+ import { EventBus } from "./EventBus";
7
+ import { ISchema } from "../interfaces/ISchema";
8
+ import { IFieldChangeEvent } from "../interfaces/IEventBus";
9
+
10
+ /**
11
+ * Engine that listens to the EventBus and evaluates cross-field dependencies.
12
+ */
13
+ export class ConditionEngine {
14
+ private schema: ISchema;
15
+
16
+ /**
17
+ * Initializes the ConditionEngine with the active schema.
18
+ * @param schema The current UI layout schema.
19
+ */
20
+ constructor(schema: ISchema) {
21
+ this.schema = schema;
22
+ this.initializeListeners();
23
+ }
24
+
25
+ /**
26
+ * Subscribes to the EventBus for real-time field change interception.
27
+ */
28
+ private initializeListeners(): void {
29
+ EventBus.getInstance().subscribe(this.handleEvent.bind(this));
30
+ }
31
+
32
+ /**
33
+ * Internal handler to evaluate business rules when a field is updated.
34
+ * @param event The structured change event.
35
+ */
36
+ private handleEvent(event: IFieldChangeEvent): void {
37
+ // Architecture hook: Logic to evaluate conditions against the schema
38
+ // and update plugin properties will be injected here in later phases.
39
+ }
40
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file Engine.ts
3
+ * @description The central orchestrator that routes data between the Layout Factory and Plugin Registry.
4
+ */
5
+
6
+ import { ISchema } from "../interfaces/ISchema";
7
+ import { ILayoutManager } from "../interfaces/ILayoutManager";
8
+ import { ConditionEngine } from "./ConditionEngine";
9
+ import Control from "sap/ui/core/Control";
10
+ import VBox from "sap/m/VBox";
11
+
12
+ /**
13
+ * The MetaUI Engine. Responsible for bootstrapping the layout generation.
14
+ * Follows strict Orchestrator patterns; delegates all actual rendering to LayoutManagers.
15
+ */
16
+ export class Engine {
17
+ private conditionEngine: ConditionEngine | null = null;
18
+
19
+ /**
20
+ * Evaluates the schema and delegates generation to the layout factory.
21
+ * @param schema The fully normalized schema definition.
22
+ * @param layoutManager The injected layout factory class.
23
+ * @returns The populated UI5 layout container.
24
+ */
25
+ public build(schema: ISchema, layoutManager: ILayoutManager): Control {
26
+ // Bootstrap the condition engine to start listening for field events in this schema
27
+ this.conditionEngine = new ConditionEngine(schema);
28
+
29
+ if (schema.mode === "form") {
30
+ return layoutManager.renderForm(schema);
31
+ } else if (schema.mode === "table") {
32
+ if (!schema.tables || schema.tables.length === 0) {
33
+ throw new Error("[MetaUI] Table mode requires at least one table definition.");
34
+ }
35
+ return layoutManager.renderTable(schema.tables[0]);
36
+ } else if (schema.mode === "mixed") {
37
+ const container = new VBox({ renderType: "Bare" });
38
+
39
+ // 1. Render the top-level form if there are root fields
40
+ if (schema.rootFields && schema.rootFields.length > 0) {
41
+ container.addItem(layoutManager.renderForm(schema));
42
+ }
43
+
44
+ // 2. Render all tables sequentially below the form
45
+ if (schema.tables && schema.tables.length > 0) {
46
+ schema.tables.forEach(tableMeta => {
47
+ container.addItem(layoutManager.renderTable(tableMeta));
48
+ });
49
+ }
50
+
51
+ return container;
52
+ }
53
+
54
+ throw new Error(`[MetaUI] Mode '${schema.mode}' is not yet implemented or invalid.`);
55
+ }
56
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @file EventBus.ts
3
+ * @description Internal pub/sub channel for managing cross-field dependencies.
4
+ */
5
+
6
+ import { IEventBus, IFieldChangeEvent } from "../interfaces/IEventBus";
7
+
8
+ /**
9
+ * Singleton orchestrator for localized state changes across the generated UI layout.
10
+ */
11
+ export class EventBus implements IEventBus {
12
+ private static instance: EventBus;
13
+ private listeners: Array<(event: IFieldChangeEvent) => void> = [];
14
+
15
+ private constructor() {
16
+ // Enforce singleton
17
+ }
18
+
19
+ /**
20
+ * Retrieves the singular instance of the EventBus.
21
+ * @returns The EventBus singleton.
22
+ */
23
+ public static getInstance(): EventBus {
24
+ if (!EventBus.instance) {
25
+ EventBus.instance = new EventBus();
26
+ }
27
+ return EventBus.instance;
28
+ }
29
+
30
+ /**
31
+ * Publishes a value change event to all internal subscribers.
32
+ * @param event The structured payload of the field change.
33
+ */
34
+ public publishFieldChange(event: IFieldChangeEvent): void {
35
+ this.listeners.forEach(listener => listener(event));
36
+ }
37
+
38
+ /**
39
+ * Registers a callback listener to react to field changes.
40
+ * @param listener The callback function.
41
+ */
42
+ public subscribe(listener: (event: IFieldChangeEvent) => void): void {
43
+ this.listeners.push(listener);
44
+ }
45
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file PluginRegistry.ts
3
+ * @description Centralized hub for mapping UI5 controls to SAP schema data types.
4
+ * Enforces the Registry Pattern to decouple the core engine from specific UI5 control implementations.
5
+ */
6
+
7
+ import { IPlugin } from "../interfaces/IPlugin";
8
+ import { FieldType } from "../interfaces/ISchema";
9
+
10
+ /**
11
+ * Singleton registry that manages the lifecycle and lookup of UI5 plugin constructors.
12
+ */
13
+ export class PluginRegistry {
14
+ private static instance: PluginRegistry;
15
+
16
+ /** Internal map linking string types to their respective constructor functions. */
17
+ private plugins: Map<FieldType, new () => IPlugin> = new Map();
18
+
19
+ private constructor() {
20
+ // Enforce singleton
21
+ }
22
+
23
+ /**
24
+ * Retrieves the singular instance of the PluginRegistry.
25
+ * @returns The PluginRegistry singleton.
26
+ */
27
+ public static getInstance(): PluginRegistry {
28
+ if (!PluginRegistry.instance) {
29
+ PluginRegistry.instance = new PluginRegistry();
30
+ }
31
+ return PluginRegistry.instance;
32
+ }
33
+
34
+ /**
35
+ * Registers a new UI control plugin against a specific schema primitive type.
36
+ * @param type The primitive FieldType (e.g., 'string', 'date').
37
+ * @param pluginConstructor The class constructor for the plugin.
38
+ */
39
+ public register(type: FieldType, pluginConstructor: new () => IPlugin): void {
40
+ this.plugins.set(type, pluginConstructor);
41
+ }
42
+
43
+ /**
44
+ * Instantiates and returns a new plugin based on the requested primitive type.
45
+ * @param type The primitive FieldType to look up.
46
+ * @throws Error if no plugin has been registered for the provided type.
47
+ * @returns A newly instantiated plugin ready for rendering.
48
+ */
49
+ public getPlugin(type: FieldType): IPlugin {
50
+ const PluginClass = this.plugins.get(type);
51
+ if (!PluginClass) {
52
+ throw new Error(`[MetaUI] No plugin registered for FieldType: ${type}`);
53
+ }
54
+ return new PluginClass();
55
+ }
56
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @file SchemaNormalizer.ts
3
+ * @description Transforms and validates incoming JSON payloads into the strict ISchema matrix.
4
+ */
5
+
6
+ import { ISchema } from "../interfaces/ISchema";
7
+
8
+ /**
9
+ * Utility class ensuring data fidelity before layout construction begins.
10
+ */
11
+ export class SchemaNormalizer {
12
+
13
+ /**
14
+ * Validates that the provided raw payload conforms to the required ISchema structures.
15
+ * @param rawSchema The heterogeneous JSON object provided to the Engine.
16
+ * @returns The strictly cast ISchema matrix.
17
+ * @throws Error if the mandatory schema properties are missing.
18
+ */
19
+ public static normalize(rawSchema: any): ISchema {
20
+ if (!rawSchema || typeof rawSchema !== 'object') {
21
+ throw new Error("[MetaUI] Invalid Schema Payload: Payload is not an object.");
22
+ }
23
+
24
+ if (!rawSchema.mode || !rawSchema.rootFields || !rawSchema.tables) {
25
+ throw new Error("[MetaUI] Invalid Schema Payload: Missing required structures (mode, rootFields, tables).");
26
+ }
27
+
28
+ // Return the strongly typed and validated schema block.
29
+ return rawSchema as ISchema;
30
+ }
31
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file StateManager.ts
3
+ * @description Isolated internal state management. Decouples the MetaUI engine from external host models.
4
+ */
5
+
6
+ import JSONModel from "sap/ui/model/json/JSONModel";
7
+ import ManagedObject from "sap/ui/base/ManagedObject";
8
+
9
+ /**
10
+ * Manages the isolated internal data payload for the MetaUI engine.
11
+ */
12
+ export class StateManager {
13
+ private model: JSONModel;
14
+
15
+ /**
16
+ * Initializes the state manager with an optional initial payload.
17
+ * @param initialData The initial JSON payload to populate the fields.
18
+ */
19
+ constructor(initialData: Record<string, any> = {}) {
20
+ // Deep copy to ensure no reference leakage from the host application
21
+ const safeData = JSON.parse(JSON.stringify(initialData));
22
+ this.model = new JSONModel(safeData);
23
+ this.model.setDefaultBindingMode("TwoWay");
24
+ }
25
+
26
+ /**
27
+ * Retrieves the isolated JSONModel instance to attach to the root GeneratorHost control.
28
+ * @returns The internal JSONModel.
29
+ */
30
+ public getModel(): JSONModel {
31
+ return this.model;
32
+ }
33
+
34
+ /**
35
+ * Binds a generated UI control property to a path in the internal model.
36
+ * @param control The target UI5 Control.
37
+ * @param property The UI5 property name (e.g., 'value').
38
+ * @param bindingPath The exact path in the JSON payload.
39
+ */
40
+ public bindControl(control: ManagedObject, property: string, bindingPath: string): void {
41
+ control.bindProperty(property, {
42
+ path: bindingPath,
43
+ model: "meta" // Assumes the model is bound to the root container with the name 'meta'
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Extracts the flat JavaScript object representing the current state of the UI.
49
+ * @returns A serialized record of the data payload, free of UI5 wrappers.
50
+ */
51
+ public extractPayload(): Record<string, any> {
52
+ // Create a deep copy to prevent accidental mutations by the host
53
+ const data = this.model.getData();
54
+ return JSON.parse(JSON.stringify(data));
55
+ }
56
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @file IEventBus.ts
3
+ * @description Pub/Sub interface for localized cross-field communication and state logic.
4
+ */
5
+
6
+ /**
7
+ * Describes a state change payload fired by a plugin.
8
+ */
9
+ export interface IFieldChangeEvent {
10
+ fieldName: string;
11
+ newValue: any;
12
+ }
13
+
14
+ /**
15
+ * Contract for the internal event orchestrator to manage conditional UI logic.
16
+ */
17
+ export interface IEventBus {
18
+ /**
19
+ * Publishes a value change event to the internal channel.
20
+ * @param event The structured event detailing which field changed and its new value.
21
+ */
22
+ publishFieldChange(event: IFieldChangeEvent): void;
23
+
24
+ /**
25
+ * Subscribes a listener to react to field value changes.
26
+ * @param listener The callback function to execute when a change occurs.
27
+ */
28
+ subscribe(listener: (event: IFieldChangeEvent) => void): void;
29
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @file ILayoutManager.ts
3
+ * @description Factory contract for generating UI5 layout containers (Forms/Tables).
4
+ */
5
+
6
+ import Control from "sap/ui/core/Control";
7
+ import { ISchema, ITableMetadata } from "./ISchema";
8
+
9
+ /**
10
+ * The factory interface responsible for structuring the visual grid.
11
+ */
12
+ export interface ILayoutManager {
13
+ /**
14
+ * Constructs a responsive form layout container utilizing native UI5 grids.
15
+ * @param schema The master schema containing rootFields to be mounted.
16
+ * @returns A populated UI5 container (e.g., sap.ui.layout.form.SimpleForm).
17
+ */
18
+ renderForm(schema: ISchema): Control;
19
+
20
+ /**
21
+ * Constructs an analytical or responsive table layout.
22
+ * @param tableMeta The metadata defining the table columns.
23
+ * @returns A UI5 Table control (e.g., sap.m.Table or sap.ui.table.Table).
24
+ */
25
+ renderTable(tableMeta: ITableMetadata): Control;
26
+ }