@siliconst/metaui 1.0.0 → 1.2.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 (189) hide show
  1. package/README.md +134 -123
  2. package/package.json +22 -34
  3. package/src/nz/co/siliconst/ui5/metaui/.library +7 -7
  4. package/src/nz/co/siliconst/ui5/metaui/controls/GeneratorHost-dbg.js +300 -0
  5. package/src/nz/co/siliconst/ui5/metaui/controls/GeneratorHost-dbg.js.map +1 -0
  6. package/src/nz/co/siliconst/ui5/metaui/controls/GeneratorHost.d.ts +130 -0
  7. package/src/nz/co/siliconst/ui5/metaui/controls/GeneratorHost.d.ts.map +1 -0
  8. package/src/nz/co/siliconst/ui5/metaui/controls/GeneratorHost.js +2 -0
  9. package/src/nz/co/siliconst/ui5/metaui/controls/GeneratorHost.js.map +1 -0
  10. package/src/nz/co/siliconst/ui5/metaui/controls/GeneratorHost.ts +292 -162
  11. package/src/nz/co/siliconst/ui5/metaui/core/ConditionEngine-dbg.js +52 -0
  12. package/src/nz/co/siliconst/ui5/metaui/core/ConditionEngine-dbg.js.map +1 -0
  13. package/src/nz/co/siliconst/ui5/metaui/core/ConditionEngine.d.ts +29 -0
  14. package/src/nz/co/siliconst/ui5/metaui/core/ConditionEngine.d.ts.map +1 -0
  15. package/src/nz/co/siliconst/ui5/metaui/core/ConditionEngine.js +2 -0
  16. package/src/nz/co/siliconst/ui5/metaui/core/ConditionEngine.js.map +1 -0
  17. package/src/nz/co/siliconst/ui5/metaui/core/ConditionEngine.ts +49 -40
  18. package/src/nz/co/siliconst/ui5/metaui/core/Engine-dbg.js +82 -0
  19. package/src/nz/co/siliconst/ui5/metaui/core/Engine-dbg.js.map +1 -0
  20. package/src/nz/co/siliconst/ui5/metaui/core/Engine.d.ts +17 -0
  21. package/src/nz/co/siliconst/ui5/metaui/core/Engine.d.ts.map +1 -0
  22. package/src/nz/co/siliconst/ui5/metaui/core/Engine.js +2 -0
  23. package/src/nz/co/siliconst/ui5/metaui/core/Engine.js.map +1 -0
  24. package/src/nz/co/siliconst/ui5/metaui/core/Engine.ts +82 -56
  25. package/src/nz/co/siliconst/ui5/metaui/core/EventBus-dbg.js +66 -0
  26. package/src/nz/co/siliconst/ui5/metaui/core/EventBus-dbg.js.map +1 -0
  27. package/src/nz/co/siliconst/ui5/metaui/core/EventBus.d.ts +40 -0
  28. package/src/nz/co/siliconst/ui5/metaui/core/EventBus.d.ts.map +1 -0
  29. package/src/nz/co/siliconst/ui5/metaui/core/EventBus.js +2 -0
  30. package/src/nz/co/siliconst/ui5/metaui/core/EventBus.js.map +1 -0
  31. package/src/nz/co/siliconst/ui5/metaui/core/EventBus.ts +60 -45
  32. package/src/nz/co/siliconst/ui5/metaui/core/PluginRegistry-dbg.js +73 -0
  33. package/src/nz/co/siliconst/ui5/metaui/core/PluginRegistry-dbg.js.map +1 -0
  34. package/src/nz/co/siliconst/ui5/metaui/core/PluginRegistry.d.ts +39 -0
  35. package/src/nz/co/siliconst/ui5/metaui/core/PluginRegistry.d.ts.map +1 -0
  36. package/src/nz/co/siliconst/ui5/metaui/core/PluginRegistry.js +2 -0
  37. package/src/nz/co/siliconst/ui5/metaui/core/PluginRegistry.js.map +1 -0
  38. package/src/nz/co/siliconst/ui5/metaui/core/PluginRegistry.ts +71 -56
  39. package/src/nz/co/siliconst/ui5/metaui/core/SchemaNormalizer-dbg.js +146 -0
  40. package/src/nz/co/siliconst/ui5/metaui/core/SchemaNormalizer-dbg.js.map +1 -0
  41. package/src/nz/co/siliconst/ui5/metaui/core/SchemaNormalizer.d.ts +25 -0
  42. package/src/nz/co/siliconst/ui5/metaui/core/SchemaNormalizer.d.ts.map +1 -0
  43. package/src/nz/co/siliconst/ui5/metaui/core/SchemaNormalizer.js +2 -0
  44. package/src/nz/co/siliconst/ui5/metaui/core/SchemaNormalizer.js.map +1 -0
  45. package/src/nz/co/siliconst/ui5/metaui/core/SchemaNormalizer.ts +157 -31
  46. package/src/nz/co/siliconst/ui5/metaui/core/StateManager-dbg.js +56 -0
  47. package/src/nz/co/siliconst/ui5/metaui/core/StateManager-dbg.js.map +1 -0
  48. package/src/nz/co/siliconst/ui5/metaui/core/StateManager.d.ts +37 -0
  49. package/src/nz/co/siliconst/ui5/metaui/core/StateManager.d.ts.map +1 -0
  50. package/src/nz/co/siliconst/ui5/metaui/core/StateManager.js +2 -0
  51. package/src/nz/co/siliconst/ui5/metaui/core/StateManager.js.map +1 -0
  52. package/src/nz/co/siliconst/ui5/metaui/core/StateManager.ts +56 -56
  53. package/src/nz/co/siliconst/ui5/metaui/interfaces/IEventBus-dbg.js +2 -0
  54. package/src/nz/co/siliconst/ui5/metaui/interfaces/IEventBus-dbg.js.map +1 -0
  55. package/src/nz/co/siliconst/ui5/metaui/interfaces/IEventBus.d.ts +29 -0
  56. package/src/nz/co/siliconst/ui5/metaui/interfaces/IEventBus.d.ts.map +1 -0
  57. package/src/nz/co/siliconst/ui5/metaui/interfaces/IEventBus.js +2 -0
  58. package/src/nz/co/siliconst/ui5/metaui/interfaces/IEventBus.js.map +1 -0
  59. package/src/nz/co/siliconst/ui5/metaui/interfaces/IEventBus.ts +29 -29
  60. package/src/nz/co/siliconst/ui5/metaui/interfaces/ILayoutManager-dbg.js +2 -0
  61. package/src/nz/co/siliconst/ui5/metaui/interfaces/ILayoutManager-dbg.js.map +1 -0
  62. package/src/nz/co/siliconst/ui5/metaui/interfaces/ILayoutManager.d.ts +20 -0
  63. package/src/nz/co/siliconst/ui5/metaui/interfaces/ILayoutManager.d.ts.map +1 -0
  64. package/src/nz/co/siliconst/ui5/metaui/interfaces/ILayoutManager.js +2 -0
  65. package/src/nz/co/siliconst/ui5/metaui/interfaces/ILayoutManager.js.map +1 -0
  66. package/src/nz/co/siliconst/ui5/metaui/interfaces/ILayoutManager.ts +21 -26
  67. package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin-dbg.js +2 -0
  68. package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin-dbg.js.map +1 -0
  69. package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin.d.ts +15 -0
  70. package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin.d.ts.map +1 -0
  71. package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin.js +2 -0
  72. package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin.js.map +1 -0
  73. package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin.ts +14 -33
  74. package/src/nz/co/siliconst/ui5/metaui/interfaces/ISchema-dbg.js +2 -0
  75. package/src/nz/co/siliconst/ui5/metaui/interfaces/ISchema-dbg.js.map +1 -0
  76. package/src/nz/co/siliconst/ui5/metaui/interfaces/ISchema.d.ts +58 -0
  77. package/src/nz/co/siliconst/ui5/metaui/interfaces/ISchema.d.ts.map +1 -0
  78. package/src/nz/co/siliconst/ui5/metaui/interfaces/ISchema.js +2 -0
  79. package/src/nz/co/siliconst/ui5/metaui/interfaces/ISchema.js.map +1 -0
  80. package/src/nz/co/siliconst/ui5/metaui/interfaces/ISchema.ts +70 -70
  81. package/src/nz/co/siliconst/ui5/metaui/layouts/BaseLayout-dbg.js +24 -0
  82. package/src/nz/co/siliconst/ui5/metaui/layouts/BaseLayout-dbg.js.map +1 -0
  83. package/src/nz/co/siliconst/ui5/metaui/layouts/BaseLayout.d.ts +15 -0
  84. package/src/nz/co/siliconst/ui5/metaui/layouts/BaseLayout.d.ts.map +1 -0
  85. package/src/nz/co/siliconst/ui5/metaui/layouts/BaseLayout.js +2 -0
  86. package/src/nz/co/siliconst/ui5/metaui/layouts/BaseLayout.js.map +1 -0
  87. package/src/nz/co/siliconst/ui5/metaui/layouts/BaseLayout.ts +22 -34
  88. package/src/nz/co/siliconst/ui5/metaui/layouts/FormLayout-dbg.js +99 -0
  89. package/src/nz/co/siliconst/ui5/metaui/layouts/FormLayout-dbg.js.map +1 -0
  90. package/src/nz/co/siliconst/ui5/metaui/layouts/FormLayout.d.ts +20 -0
  91. package/src/nz/co/siliconst/ui5/metaui/layouts/FormLayout.d.ts.map +1 -0
  92. package/src/nz/co/siliconst/ui5/metaui/layouts/FormLayout.js +2 -0
  93. package/src/nz/co/siliconst/ui5/metaui/layouts/FormLayout.js.map +1 -0
  94. package/src/nz/co/siliconst/ui5/metaui/layouts/FormLayout.ts +97 -60
  95. package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout-dbg.js +103 -0
  96. package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout-dbg.js.map +1 -0
  97. package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout.d.ts +16 -0
  98. package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout.d.ts.map +1 -0
  99. package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout.js +2 -0
  100. package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout.js.map +1 -0
  101. package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout.ts +119 -102
  102. package/src/nz/co/siliconst/ui5/metaui/library-dbg.js +44 -0
  103. package/src/nz/co/siliconst/ui5/metaui/library-dbg.js.map +1 -0
  104. package/src/nz/co/siliconst/ui5/metaui/library-preload.js +39 -0
  105. package/src/nz/co/siliconst/ui5/metaui/library-preload.js.map +1 -0
  106. package/src/nz/co/siliconst/ui5/metaui/library.d.ts +10 -0
  107. package/src/nz/co/siliconst/ui5/metaui/library.d.ts.map +1 -0
  108. package/src/nz/co/siliconst/ui5/metaui/library.js +2 -0
  109. package/src/nz/co/siliconst/ui5/metaui/library.js.map +1 -0
  110. package/src/nz/co/siliconst/ui5/metaui/library.ts +55 -43
  111. package/src/nz/co/siliconst/ui5/metaui/manifest.json +35 -0
  112. package/src/nz/co/siliconst/ui5/metaui/plugins/ArrayPlugin-dbg.js +68 -0
  113. package/src/nz/co/siliconst/ui5/metaui/plugins/ArrayPlugin-dbg.js.map +1 -0
  114. package/src/nz/co/siliconst/ui5/metaui/plugins/ArrayPlugin.d.ts +15 -0
  115. package/src/nz/co/siliconst/ui5/metaui/plugins/ArrayPlugin.d.ts.map +1 -0
  116. package/src/nz/co/siliconst/ui5/metaui/plugins/ArrayPlugin.js +2 -0
  117. package/src/nz/co/siliconst/ui5/metaui/plugins/ArrayPlugin.js.map +1 -0
  118. package/src/nz/co/siliconst/ui5/metaui/plugins/ArrayPlugin.ts +71 -0
  119. package/src/nz/co/siliconst/ui5/metaui/plugins/BasePlugin-dbg.js +55 -0
  120. package/src/nz/co/siliconst/ui5/metaui/plugins/BasePlugin-dbg.js.map +1 -0
  121. package/src/nz/co/siliconst/ui5/metaui/plugins/BasePlugin.d.ts +24 -0
  122. package/src/nz/co/siliconst/ui5/metaui/plugins/BasePlugin.d.ts.map +1 -0
  123. package/src/nz/co/siliconst/ui5/metaui/plugins/BasePlugin.js +2 -0
  124. package/src/nz/co/siliconst/ui5/metaui/plugins/BasePlugin.js.map +1 -0
  125. package/src/nz/co/siliconst/ui5/metaui/plugins/BasePlugin.ts +56 -58
  126. package/src/nz/co/siliconst/ui5/metaui/plugins/BooleanPlugin-dbg.js +42 -0
  127. package/src/nz/co/siliconst/ui5/metaui/plugins/BooleanPlugin-dbg.js.map +1 -0
  128. package/src/nz/co/siliconst/ui5/metaui/plugins/BooleanPlugin.d.ts +18 -0
  129. package/src/nz/co/siliconst/ui5/metaui/plugins/BooleanPlugin.d.ts.map +1 -0
  130. package/src/nz/co/siliconst/ui5/metaui/plugins/BooleanPlugin.js +2 -0
  131. package/src/nz/co/siliconst/ui5/metaui/plugins/BooleanPlugin.js.map +1 -0
  132. package/src/nz/co/siliconst/ui5/metaui/plugins/BooleanPlugin.ts +41 -42
  133. package/src/nz/co/siliconst/ui5/metaui/plugins/DatePlugin-dbg.js +69 -0
  134. package/src/nz/co/siliconst/ui5/metaui/plugins/DatePlugin-dbg.js.map +1 -0
  135. package/src/nz/co/siliconst/ui5/metaui/plugins/DatePlugin.d.ts +18 -0
  136. package/src/nz/co/siliconst/ui5/metaui/plugins/DatePlugin.d.ts.map +1 -0
  137. package/src/nz/co/siliconst/ui5/metaui/plugins/DatePlugin.js +2 -0
  138. package/src/nz/co/siliconst/ui5/metaui/plugins/DatePlugin.js.map +1 -0
  139. package/src/nz/co/siliconst/ui5/metaui/plugins/DatePlugin.ts +69 -63
  140. package/src/nz/co/siliconst/ui5/metaui/plugins/DateTimePlugin-dbg.js +68 -0
  141. package/src/nz/co/siliconst/ui5/metaui/plugins/DateTimePlugin-dbg.js.map +1 -0
  142. package/src/nz/co/siliconst/ui5/metaui/plugins/DateTimePlugin.d.ts +18 -0
  143. package/src/nz/co/siliconst/ui5/metaui/plugins/DateTimePlugin.d.ts.map +1 -0
  144. package/src/nz/co/siliconst/ui5/metaui/plugins/DateTimePlugin.js +2 -0
  145. package/src/nz/co/siliconst/ui5/metaui/plugins/DateTimePlugin.js.map +1 -0
  146. package/src/nz/co/siliconst/ui5/metaui/plugins/DateTimePlugin.ts +68 -0
  147. package/src/nz/co/siliconst/ui5/metaui/plugins/DropdownPlugin-dbg.js +67 -0
  148. package/src/nz/co/siliconst/ui5/metaui/plugins/DropdownPlugin-dbg.js.map +1 -0
  149. package/src/nz/co/siliconst/ui5/metaui/plugins/DropdownPlugin.d.ts +18 -0
  150. package/src/nz/co/siliconst/ui5/metaui/plugins/DropdownPlugin.d.ts.map +1 -0
  151. package/src/nz/co/siliconst/ui5/metaui/plugins/DropdownPlugin.js +2 -0
  152. package/src/nz/co/siliconst/ui5/metaui/plugins/DropdownPlugin.js.map +1 -0
  153. package/src/nz/co/siliconst/ui5/metaui/plugins/DropdownPlugin.ts +65 -65
  154. package/src/nz/co/siliconst/ui5/metaui/plugins/NumberPlugin-dbg.js +56 -0
  155. package/src/nz/co/siliconst/ui5/metaui/plugins/NumberPlugin-dbg.js.map +1 -0
  156. package/src/nz/co/siliconst/ui5/metaui/plugins/NumberPlugin.d.ts +18 -0
  157. package/src/nz/co/siliconst/ui5/metaui/plugins/NumberPlugin.d.ts.map +1 -0
  158. package/src/nz/co/siliconst/ui5/metaui/plugins/NumberPlugin.js +2 -0
  159. package/src/nz/co/siliconst/ui5/metaui/plugins/NumberPlugin.js.map +1 -0
  160. package/src/nz/co/siliconst/ui5/metaui/plugins/NumberPlugin.ts +56 -55
  161. package/src/nz/co/siliconst/ui5/metaui/plugins/StringPlugin-dbg.js +57 -0
  162. package/src/nz/co/siliconst/ui5/metaui/plugins/StringPlugin-dbg.js.map +1 -0
  163. package/src/nz/co/siliconst/ui5/metaui/plugins/StringPlugin.d.ts +18 -0
  164. package/src/nz/co/siliconst/ui5/metaui/plugins/StringPlugin.d.ts.map +1 -0
  165. package/src/nz/co/siliconst/ui5/metaui/plugins/StringPlugin.js +2 -0
  166. package/src/nz/co/siliconst/ui5/metaui/plugins/StringPlugin.js.map +1 -0
  167. package/src/nz/co/siliconst/ui5/metaui/plugins/StringPlugin.ts +58 -55
  168. package/src/nz/co/siliconst/ui5/metaui/plugins/SwitchPlugin-dbg.js +43 -0
  169. package/src/nz/co/siliconst/ui5/metaui/plugins/SwitchPlugin-dbg.js.map +1 -0
  170. package/src/nz/co/siliconst/ui5/metaui/plugins/SwitchPlugin.d.ts +18 -0
  171. package/src/nz/co/siliconst/ui5/metaui/plugins/SwitchPlugin.d.ts.map +1 -0
  172. package/src/nz/co/siliconst/ui5/metaui/plugins/SwitchPlugin.js +2 -0
  173. package/src/nz/co/siliconst/ui5/metaui/plugins/SwitchPlugin.js.map +1 -0
  174. package/src/nz/co/siliconst/ui5/metaui/plugins/SwitchPlugin.ts +42 -0
  175. package/src/nz/co/siliconst/ui5/metaui/plugins/TextAreaPlugin-dbg.js +58 -0
  176. package/src/nz/co/siliconst/ui5/metaui/plugins/TextAreaPlugin-dbg.js.map +1 -0
  177. package/src/nz/co/siliconst/ui5/metaui/plugins/TextAreaPlugin.d.ts +18 -0
  178. package/src/nz/co/siliconst/ui5/metaui/plugins/TextAreaPlugin.d.ts.map +1 -0
  179. package/src/nz/co/siliconst/ui5/metaui/plugins/TextAreaPlugin.js +2 -0
  180. package/src/nz/co/siliconst/ui5/metaui/plugins/TextAreaPlugin.js.map +1 -0
  181. package/src/nz/co/siliconst/ui5/metaui/plugins/TextAreaPlugin.ts +58 -0
  182. package/src/nz/co/siliconst/ui5/metaui/plugins/TimePlugin-dbg.js +74 -0
  183. package/src/nz/co/siliconst/ui5/metaui/plugins/TimePlugin-dbg.js.map +1 -0
  184. package/src/nz/co/siliconst/ui5/metaui/plugins/TimePlugin.d.ts +18 -0
  185. package/src/nz/co/siliconst/ui5/metaui/plugins/TimePlugin.d.ts.map +1 -0
  186. package/src/nz/co/siliconst/ui5/metaui/plugins/TimePlugin.js +2 -0
  187. package/src/nz/co/siliconst/ui5/metaui/plugins/TimePlugin.js.map +1 -0
  188. package/src/nz/co/siliconst/ui5/metaui/plugins/TimePlugin.ts +71 -0
  189. package/ui5.yaml +4 -12
package/README.md CHANGED
@@ -1,123 +1,134 @@
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!*
1
+ # MetaUI
2
+
3
+ **MetaUI** is a metadata-driven UI5 rendering engine. It provides a mechanism to generate SAP Fiori architectures at runtime by parsing standard JSON Schema definitions, reducing the requirement for manual XML View construction.
4
+
5
+ Built for strict UI5 architecture adherence, MetaUI enforces modularity through a Plugin-based design and explicitly avoids inline DOM manipulation.
6
+
7
+ ✨ **[Play with the Live Interactive Demo Here!](https://SiliconStreetDev1.github.io/MetaUI/index.html)** ✨
8
+
9
+ ---
10
+
11
+ ## 🏗 Architecture & Design Tenets
12
+
13
+ 1. **JSON-Schema Core:** MetaUI parses standard recursive JSON Schema `properties` blocks (`Record<string, IPropertyMetadata>`).
14
+ 2. **Implicit Layout Orchestration:** The `Engine` determines standard Layouts based on payload shape. Root `object` types map to `FormLayout` clusters. Root `array` types map to `TableLayout` instances. Nested arrays trigger recursive drill-down dialogs.
15
+ 3. **ConditionEngine Compilation:** Cross-context visibility rules (e.g., `"$root.status === 'DRAFT'"`) are transpiled into native UI5 Expression Bindings to utilize native reactivity.
16
+ 4. **Strict Plugin Pipeline:** The `PluginRegistry` isolates control instantiation. Core layout managers delegate to registered implementations of `IPlugin`.
17
+
18
+ ---
19
+
20
+ ## 🚀 Quick Start (Sandbox)
21
+
22
+ The repository contains an internal UI5 sandbox for testing the engine's inference logic.
23
+
24
+ ```bash
25
+ # Install dependencies
26
+ npm install
27
+
28
+ # Run the UI5 development server (Sandbox application)
29
+ npm run start
30
+ ```
31
+ The test suite will boot at `http://localhost:8080/index.html`.
32
+
33
+ ---
34
+
35
+ ## 🛠 Integration
36
+
37
+ ### Installation
38
+
39
+ Import the compiled library namespace into your UI5 project.
40
+
41
+ ```javascript
42
+ sap.ui.require(["nz/co/siliconst/ui5/metaui/controls/GeneratorHost"], function (GeneratorHost) {
43
+ const host = new GeneratorHost({
44
+ // The Schema Definition
45
+ schemaDefinition: {
46
+ type: "object",
47
+ properties: {
48
+ Username: { type: "string", ui: { label: "User Name" } },
49
+ IsActive: { type: "boolean", ui: { label: "Active", widget: "switch" } }
50
+ }
51
+ },
52
+ // The Data Payload
53
+ initialData: {
54
+ Username: "JDoe",
55
+ IsActive: true
56
+ }
57
+ });
58
+
59
+ // Option A: Render inside an existing container
60
+ myVBox.addItem(host);
61
+
62
+ // Option B: Mount as an isolated Dialog
63
+ host.openInDialog("User Configuration", "Save");
64
+
65
+ // Capture the extracted JSON payload on submit
66
+ host.attachSubmit(function(oEvent) {
67
+ const payload = oEvent.getParameter("payload");
68
+ console.log(payload);
69
+ });
70
+ });
71
+ ```
72
+
73
+ ### Declarative XML Binding
74
+
75
+ MetaUI supports client-side validation based on JSON Schema constraints natively.
76
+
77
+ ### Mandatory Fields
78
+ Adding `"required": true` to any property in the schema will enforce input validation prior to the `submit` event being fired.
79
+
80
+ ### Custom Validation Exits (`beforeSubmit`)
81
+ For business logic requiring custom checks (e.g., async validation), the `GeneratorHost` provides a `beforeSubmit` event.
82
+
83
+ ```javascript
84
+ onBeforeSubmit: function(oEvent) {
85
+ const payload = oEvent.getParameter("payload");
86
+ const preventDefault = oEvent.getParameter("preventDefault");
87
+ const addError = oEvent.getParameter("addError");
88
+
89
+ if (payload.CustomerName === "Reserved") {
90
+ addError("CustomerName", "This customer name is reserved.");
91
+ preventDefault();
92
+ }
93
+ }
94
+ ```
95
+
96
+ ### Fiori Message Manager Integration
97
+ By default, validation errors are shown locally. To aggregate validation errors into the global UI5 Message Popover, enable `useMessageManager` on the `GeneratorHost`:
98
+
99
+ ```xml
100
+ <meta:GeneratorHost
101
+ schemaDefinition="{/mySchema}"
102
+ useMessageManager="true"
103
+ beforeSubmit=".onBeforeSubmit"
104
+ submit=".onSubmit" />
105
+ ```
106
+
107
+ ## Supported Features
108
+
109
+ The `GeneratorHost` fully supports declarative XML instantiation in traditional UI5 views:
110
+
111
+ ```xml
112
+ <mvc:View
113
+ xmlns:mvc="sap.ui.core.mvc"
114
+ xmlns:meta="nz.co.siliconst.ui5.metaui.controls">
115
+
116
+ <meta:GeneratorHost
117
+ id="metaHost"
118
+ schemaDefinition="{backend>/schema}"
119
+ initialData="{backend>/data}"
120
+ submit=".onFormSubmit" />
121
+
122
+ </mvc:View>
123
+ ```
124
+
125
+ ---
126
+
127
+ ## 📚 Technical Documentation
128
+
129
+ For detailed information regarding the JSON Schema properties, `ui.*` orchestrations, and recursive nested arrays, consult the payload formats specification:
130
+
131
+ - [Payload Formats & Directives](docs/payload_formats.md)
132
+ - [Fiori Integration Guide](docs/fiori_integration.md)
133
+
134
+
package/package.json CHANGED
@@ -1,35 +1,23 @@
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
- }
1
+ {
2
+ "name": "@siliconst/metaui",
3
+ "version": "1.2.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
+ "test-resources",
19
+ "ui5.yaml",
20
+ "README.md"
21
+ ],
22
+ "types": "index.d.ts"
35
23
  }
@@ -1,7 +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>
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,300 @@
1
+ sap.ui.define(["sap/ui/core/Control", "sap/m/Dialog", "sap/m/Button", "sap/m/MessagePage", "sap/base/Log", "../core/SchemaNormalizer", "../core/Engine", "../core/StateManager", "../layouts/BaseLayout"], function (Control, Dialog, Button, MessagePage, Log, ___core_SchemaNormalizer, ___core_Engine, ___core_StateManager, ___layouts_BaseLayout) {
2
+ "use strict";
3
+
4
+ const SchemaNormalizer = ___core_SchemaNormalizer["SchemaNormalizer"];
5
+ const Engine = ___core_Engine["Engine"];
6
+ const StateManager = ___core_StateManager["StateManager"];
7
+ const BaseLayout = ___layouts_BaseLayout["BaseLayout"];
8
+ /**
9
+ * Wrapper element for embedding or popping the dynamic form.
10
+ *
11
+ * @namespace nz.co.siliconst.ui5.metaui.controls
12
+ */
13
+ const GeneratorHost = Control.extend("nz.co.siliconst.ui5.metaui.controls.GeneratorHost", {
14
+ constructor: function constructor() {
15
+ Control.prototype.constructor.apply(this, arguments);
16
+ this.stateManager = null;
17
+ this.generatedContent = null;
18
+ this.engine = null;
19
+ },
20
+ /**
21
+ * Standard UI5 control renderer. Renders the generated internal content.
22
+ */
23
+ renderer: {
24
+ apiVersion: 2,
25
+ render(rm, control) {
26
+ rm.openStart("div", control);
27
+ rm.style("width", "100%");
28
+ rm.style("height", "100%");
29
+ rm.openEnd();
30
+ const content = control.getAggregation("_content");
31
+ if (content) {
32
+ rm.renderControl(content);
33
+ }
34
+ rm.close("div");
35
+ }
36
+ },
37
+ metadata: {
38
+ properties: {
39
+ /** The raw JSON metadata schema provided by the ABAP backend. */
40
+ schemaDefinition: {
41
+ type: "any",
42
+ defaultValue: null
43
+ },
44
+ /** The initial data payload to populate the fields as an object. */
45
+ initialData: {
46
+ type: "any",
47
+ defaultValue: null
48
+ },
49
+ /** The initial data payload provided as a raw JSON string. */
50
+ initialDataJson: {
51
+ type: "string",
52
+ defaultValue: null
53
+ },
54
+ /** The final payload data object, updated on submit. */
55
+ outputData: {
56
+ type: "object",
57
+ defaultValue: null
58
+ },
59
+ /** The final payload JSON string, updated on submit. */
60
+ outputDataJson: {
61
+ type: "string",
62
+ defaultValue: null
63
+ },
64
+ /** If true, validation errors will be pushed to the global sap.ui.core.message.MessageManager. */
65
+ useMessageManager: {
66
+ type: "boolean",
67
+ defaultValue: false
68
+ }
69
+ },
70
+ aggregations: {
71
+ /** The internal hidden container holding the generated layouts. */
72
+ _content: {
73
+ type: "sap.ui.core.Control",
74
+ multiple: false,
75
+ visibility: "hidden"
76
+ }
77
+ },
78
+ events: {
79
+ /** Triggered before final submission, allowing host apps to run custom validation logic and halt submission if needed. */
80
+ beforeSubmit: {
81
+ parameters: {
82
+ /** The clean extracted JSON payload representing the current state. */
83
+ payload: {
84
+ type: "object"
85
+ },
86
+ /** Function to add a custom validation error message for a specific field path. */
87
+ addError: {
88
+ type: "function"
89
+ },
90
+ /** Function to prevent the default submission and dialog closure. */
91
+ preventDefault: {
92
+ type: "function"
93
+ }
94
+ }
95
+ },
96
+ /** Triggered when the user submits the form via the Dialog modality. */
97
+ submit: {
98
+ parameters: {
99
+ /** The clean extracted JSON payload representing the current state. */
100
+ payload: {
101
+ type: "object"
102
+ },
103
+ /** The stringified final JSON payload. */
104
+ payloadJson: {
105
+ type: "string"
106
+ }
107
+ }
108
+ },
109
+ /** Triggered if the dialog is cancelled. */
110
+ cancel: {}
111
+ }
112
+ },
113
+ /**
114
+ * Initializes the UI5 control.
115
+ */
116
+ init: function _init() {
117
+ Control.prototype.init.call(this);
118
+ },
119
+ /**
120
+ * Standard UI5 lifecycle hook for destruction.
121
+ * Guaranteed to tear down isolated models and unbind global event listeners.
122
+ */
123
+ exit: function _exit() {
124
+ if (this.engine) {
125
+ this.engine.destroy();
126
+ this.engine = null;
127
+ }
128
+ if (this.stateManager) {
129
+ this.stateManager.getModel().destroy();
130
+ this.stateManager = null;
131
+ }
132
+ if (this.generatedContent) {
133
+ this.generatedContent.destroy();
134
+ this.generatedContent = null;
135
+ }
136
+ },
137
+ /**
138
+ * Lifecycle hook to automatically generate the layout if bound declaratively in XML.
139
+ */
140
+ onBeforeRendering: function _onBeforeRendering() {
141
+ if (!this.generatedContent && this.getProperty("schemaDefinition")) {
142
+ this.generate();
143
+ }
144
+ },
145
+ /**
146
+ * Extracts the payload, pushes it to properties, and fires the submit event manually.
147
+ * Returns true if submission was successful, false if validation failed or was prevented.
148
+ */
149
+ triggerSubmit: function _triggerSubmit() {
150
+ if (this.stateManager && this.engine) {
151
+ let isPrevented = false;
152
+ const useMessageManager = this.getProperty("useMessageManager");
153
+ let messageManager = null;
154
+ if (useMessageManager) {
155
+ messageManager = sap.ui.getCore().getMessageManager();
156
+ messageManager.removeAllMessages();
157
+ }
158
+
159
+ // Helper to add messages securely via async require so it isn't a hard dependency
160
+ const pushMessage = (path, text) => {
161
+ if (useMessageManager && messageManager) {
162
+ sap.ui.require(["sap/ui/core/message/Message"], Message => {
163
+ messageManager.addMessages(new Message({
164
+ message: text,
165
+ type: "Error",
166
+ target: `${this.stateManager?.getModel().getId()}>/${path}`,
167
+ processor: this.stateManager?.getModel()
168
+ }));
169
+ });
170
+ }
171
+ };
172
+
173
+ // 1. Run global schema validation
174
+ if (!this.engine.validateAll()) {
175
+ pushMessage("", "One or more fields failed schema validation. Please review the highlighted fields.");
176
+ return false; // Schema validation failed, inputs already turned red
177
+ }
178
+ const payload = this.stateManager.extractPayload();
179
+ const payloadJson = JSON.stringify(payload);
180
+
181
+ // 2. Fire beforeSubmit exit
182
+ this.fireEvent("beforeSubmit", {
183
+ payload,
184
+ preventDefault: () => {
185
+ isPrevented = true;
186
+ },
187
+ addError: (propertyPath, errorMessage) => {
188
+ isPrevented = true;
189
+ if (useMessageManager && messageManager) {
190
+ pushMessage(propertyPath, errorMessage);
191
+ } else {
192
+ Log.warning(`[MetaUI] Validation Error for ${propertyPath}: ${errorMessage}`, "GeneratorHost");
193
+ }
194
+ }
195
+ });
196
+ if (isPrevented) {
197
+ return false; // Exit validation failed or prevented submission
198
+ }
199
+
200
+ // Push final values to properties (updating XML bindings)
201
+ this.setProperty("outputData", payload, true);
202
+ this.setProperty("outputDataJson", payloadJson, true);
203
+ this.fireEvent("submit", {
204
+ payload,
205
+ payloadJson
206
+ });
207
+ return true;
208
+ }
209
+ return false;
210
+ },
211
+ /**
212
+ * Triggers the engine to parse the schema and build the layout.
213
+ * Safely wrapped in a try/catch to prevent Launchpad crashes.
214
+ */
215
+ generate: function _generate() {
216
+ try {
217
+ const rawSchema = this.getProperty("schemaDefinition");
218
+ let initialData = this.getProperty("initialData");
219
+ const initialDataJson = this.getProperty("initialDataJson");
220
+ if (initialDataJson) {
221
+ try {
222
+ initialData = JSON.parse(initialDataJson);
223
+ } catch (e) {
224
+ Log.error("[MetaUI] Failed to parse initialDataJson string.", e.message, "GeneratorHost");
225
+ }
226
+ } else if (typeof initialData === "string") {
227
+ try {
228
+ initialData = JSON.parse(initialData);
229
+ } catch (e) {
230
+ Log.error("[MetaUI] Failed to parse initialData string.", e.message, "GeneratorHost");
231
+ }
232
+ }
233
+
234
+ // 1. Normalize schema (allows fallback to data inference)
235
+ const normalizedSchema = SchemaNormalizer.normalize(rawSchema, initialData);
236
+
237
+ // 2. Bootstrap isolated State Manager
238
+ this.stateManager = new StateManager(initialData || {});
239
+
240
+ // Dynamically generate a 100% unique model name for isolation
241
+ const modelName = "metaUI_" + this.getId();
242
+ this.setModel(this.stateManager.getModel(), modelName);
243
+
244
+ // 3. Delegate to Layout Engine
245
+ this.engine = new Engine();
246
+ const layoutFactory = new BaseLayout();
247
+ this.generatedContent = this.engine.build(normalizedSchema, layoutFactory, modelName);
248
+
249
+ // 4. Mount into hidden aggregation
250
+ this.setAggregation("_content", this.generatedContent);
251
+ } catch (error) {
252
+ Log.error("[MetaUI] Engine failed to generate layout. Preventing Launchpad crash.", error.message, "GeneratorHost");
253
+ const errorPage = new MessagePage({
254
+ text: "UI Generation Failed",
255
+ description: "The dynamic schema could not be rendered. Please check the Fiori console for diagnostic logs.",
256
+ icon: "sap-icon://error"
257
+ });
258
+ this.setAggregation("_content", errorPage);
259
+ }
260
+ },
261
+ /**
262
+ * Dialog Modality: Opens the generated layout inside a sap.m.Dialog.
263
+ * Automatically wires up 'Save' and 'Cancel' buttons.
264
+ */
265
+ openInDialog: function _openInDialog(title = "Dynamic Form", submitButtonText = "Save") {
266
+ if (!this.generatedContent) {
267
+ this.generate();
268
+ }
269
+ const dialog = new Dialog({
270
+ title: title,
271
+ contentWidth: "800px",
272
+ content: [this],
273
+ // Mount self inside the dialog
274
+ beginButton: new Button({
275
+ text: submitButtonText,
276
+ type: "Emphasized",
277
+ press: () => {
278
+ const success = this.triggerSubmit();
279
+ if (success) {
280
+ dialog.close();
281
+ }
282
+ }
283
+ }),
284
+ endButton: new Button({
285
+ text: "Cancel",
286
+ press: () => {
287
+ this.fireEvent("cancel");
288
+ dialog.close();
289
+ }
290
+ }),
291
+ afterClose: () => {
292
+ dialog.destroy();
293
+ }
294
+ });
295
+ dialog.open();
296
+ }
297
+ });
298
+ return GeneratorHost;
299
+ });
300
+ //# sourceMappingURL=GeneratorHost-dbg.js.map